blob: dada37b5e0d274867639e30319ee03d5c4879e8e [file] [edit]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::io::Read;
use std::mem;
use std::os::unix::io::FromRawFd;
use std::{fs::File, rc::Rc};
use super::event::*;
use super::{bindings, VdaConnection};
use crate::error::*;
use crate::format::{BufferFd, FramePlane, PixelFormat, Profile};
/// Represents a decode session.
pub struct Session {
// Ensures the VDA connection remains open for as long as there are active sessions.
connection: Rc<VdaConnection>,
// Pipe file to be notified decode session events.
pipe: File,
session_ptr: *mut bindings::vda_session_info_t,
}
impl Session {
/// Creates a new `Session`.
pub(super) fn new(connection: &Rc<VdaConnection>, profile: Profile) -> Option<Self> {
// Safe because `conn_ptr()` is valid and won't be invalidated by `init_decode_session()`.
let session_ptr: *mut bindings::vda_session_info_t = unsafe {
bindings::init_decode_session(connection.conn_ptr(), profile.to_raw_profile())
};
if session_ptr.is_null() {
return None;
}
// Dereferencing `session_ptr` is safe because it is a valid pointer to a FD provided by
// libvda. We need to dup() the `event_pipe_fd` because File object close() the FD while
// libvda also close() it when `close_decode_session` is called.
let pipe = unsafe { File::from_raw_fd(libc::dup((*session_ptr).event_pipe_fd)) };
Some(Session {
connection: Rc::clone(connection),
pipe,
session_ptr,
})
}
/// Gets a reference of pipe that notifies events from VDA session.
pub fn pipe(&self) -> &File {
&self.pipe
}
/// Reads an `Event` object from a pipe provided a decode session.
pub fn read_event(&mut self) -> Result<Event> {
const BUF_SIZE: usize = mem::size_of::<bindings::vda_event_t>();
let mut buf = [0u8; BUF_SIZE];
self.pipe
.read_exact(&mut buf)
.map_err(Error::ReadEventFailure)?;
// Safe because libvda must have written vda_event_t to the pipe.
let vda_event = unsafe { mem::transmute::<[u8; BUF_SIZE], bindings::vda_event_t>(buf) };
// Safe because `vda_event` is a value read from `self.pipe`.
unsafe { Event::new(vda_event) }
}
/// Sends a decode request for a bitstream buffer given as `fd`.
///
/// `fd` will be closed by Chrome after decoding has occurred.
pub fn decode(
&self,
bitstream_id: i32,
fd: BufferFd,
offset: u32,
bytes_used: u32,
) -> Result<()> {
// Safe because `session_ptr` is valid and a libvda's API is called properly.
let r = unsafe {
bindings::vda_decode(
(*self.session_ptr).ctx,
bitstream_id,
fd,
offset,
bytes_used,
)
};
Response::new(r).into()
}
/// Sets the number of expected output buffers.
///
/// This function must be called after `Event::ProvidePictureBuffers` are notified.
/// After calling this function, `user_output_buffer` must be called `num_output_buffers` times.
pub fn set_output_buffer_count(&self, num_output_buffers: usize) -> Result<()> {
// Safe because `session_ptr` is valid and a libvda's API is called properly.
let r = unsafe {
bindings::vda_set_output_buffer_count((*self.session_ptr).ctx, num_output_buffers)
};
Response::new(r).into()
}
/// Provides an output buffer that will be filled with decoded frames.
///
/// Users calls this function after `set_output_buffer_count`. Then, libvda
/// will fill next frames in the buffer and noitify `Event::PictureReady`.
///
/// This function is also used to notify that they consumed decoded frames
/// in the output buffer.
///
/// This function takes ownership of `output_buffer`.
pub fn use_output_buffer(
&self,
picture_buffer_id: i32,
format: PixelFormat,
output_buffer: BufferFd,
planes: &[FramePlane],
modifier: u64,
) -> Result<()> {
let mut planes: Vec<_> = planes.iter().map(FramePlane::to_raw_frame_plane).collect();
// Safe because `session_ptr` is valid and a libvda's API is called properly.
let r = unsafe {
bindings::vda_use_output_buffer(
(*self.session_ptr).ctx,
picture_buffer_id,
format.to_raw_pixel_format(),
output_buffer,
planes.len(),
planes.as_mut_ptr(),
modifier,
)
};
Response::new(r).into()
}
/// Returns an output buffer for reuse.
///
/// `picture_buffer_id` must be a value for which `use_output_buffer` has been called already.
pub fn reuse_output_buffer(&self, picture_buffer_id: i32) -> Result<()> {
// Safe because `session_ptr` is valid and a libvda's API is called properly.
let r = unsafe {
bindings::vda_reuse_output_buffer((*self.session_ptr).ctx, picture_buffer_id)
};
Response::new(r).into()
}
/// Flushes the decode session.
///
/// When this operation has completed, `Event::FlushResponse` will be notified.
pub fn flush(&self) -> Result<()> {
// Safe because `session_ptr` is valid and a libvda's API is called properly.
let r = unsafe { bindings::vda_flush((*self.session_ptr).ctx) };
Response::new(r).into()
}
/// Resets the decode session.
///
/// When this operation has completed, Event::ResetResponse will be notified.
pub fn reset(&self) -> Result<()> {
// Safe because `session_ptr` is valid and a libvda's API is called properly.
let r = unsafe { bindings::vda_reset((*self.session_ptr).ctx) };
Response::new(r).into()
}
}
impl Drop for Session {
fn drop(&mut self) {
// Safe because `session_ptr` is unchanged from the time `new` was called, and
// `connection` also guarantees that the pointer returned by `conn_ptr()` is a valid
// connection to a VDA instance.
unsafe {
bindings::close_decode_session(self.connection.conn_ptr(), self.session_ptr);
}
}
}