blob: 23d733aac3dbe1da3680fa1ba219456dd02ba2e3 [file] [log] [blame]
// 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::fs::File;
use std::io::Read;
use std::marker::PhantomData;
use std::mem;
use std::os::raw::c_void;
use std::os::unix::io::FromRawFd;
use super::bindings;
use super::event::*;
use super::vea_instance::{Config, VeaInstance};
use crate::error::*;
use crate::format::{BufferFd, FramePlane};
pub type VeaInputBufferId = bindings::vea_input_buffer_id_t;
pub type VeaOutputBufferId = bindings::vea_output_buffer_id_t;
/// Represents an encode session.
pub struct Session<'a> {
// Pipe file to be notified encode session events.
pipe: File,
vea_ptr: *mut c_void,
raw_ptr: *mut bindings::vea_session_info_t,
// `phantom` guarantees that `Session` will not outlive the lifetime of
// `VeaInstance` that owns `vea_ptr`.
phantom: PhantomData<&'a VeaInstance>,
}
fn convert_error_code(code: i32) -> Result<()> {
if code == 0 {
Ok(())
} else {
Err(Error::EncodeSessionFailure(code))
}
}
impl<'a> Session<'a> {
/// Creates a new `Session`.
///
/// This function is safe if `vea_ptr` is a non-NULL pointer obtained from
/// `bindings::initialize_encode`.
pub(crate) unsafe fn new(vea_ptr: *mut c_void, config: Config) -> Option<Self> {
// `init_encode_session` is safe if `vea_ptr` is a non-NULL pointer from
// `bindings::initialize`.
let raw_ptr: *mut bindings::vea_session_info_t =
bindings::init_encode_session(vea_ptr, &mut config.to_raw_config());
if raw_ptr.is_null() {
return None;
}
// Dereferencing `raw_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_encode_session` is called.
let pipe = File::from_raw_fd(libc::dup((*raw_ptr).event_pipe_fd));
Some(Session {
pipe,
vea_ptr,
raw_ptr,
phantom: PhantomData,
})
}
/// Returns a reference for the pipe that notifies of encode events.
pub fn pipe(&self) -> &File {
&self.pipe
}
/// Reads an `Event` object from a pipe provided by an encode session.
pub fn read_event(&mut self) -> Result<Event> {
const BUF_SIZE: usize = mem::size_of::<bindings::vea_event_t>();
let mut buf = [0u8; BUF_SIZE];
self.pipe
.read_exact(&mut buf)
.map_err(Error::ReadEventFailure)?;
// Safe because libvda must have written vea_event_t to the pipe.
let vea_event = unsafe { mem::transmute::<[u8; BUF_SIZE], bindings::vea_event_t>(buf) };
// Safe because `vea_event` is a value read from `self.pipe`.
unsafe { Event::new(vea_event) }
}
/// Sends an encode request for an input buffer given as `fd` with planes described
/// by `planes. The timestamp of the frame to encode is typically provided in
/// milliseconds by `timestamp`. `force_keyframe` indicates to the encoder that
/// the frame should be encoded as a keyframe.
///
/// When the input buffer has been filled, an `EncoderEvent::ProcessedInputBuffer`
/// event can be read from the event pipe.
///
/// The caller is responsible for passing in a unique value for `input_buffer_id`
/// which can be referenced when the event is received.
///
/// `fd` will be closed after encoding has occurred.
pub fn encode(
&self,
input_buffer_id: VeaInputBufferId,
fd: BufferFd,
planes: &[FramePlane],
timestamp: i64,
force_keyframe: bool,
) -> Result<()> {
let mut planes: Vec<_> = planes.iter().map(FramePlane::to_raw_frame_plane).collect();
// Safe because `raw_ptr` is valid and libvda's encode API is called properly.
let r = unsafe {
bindings::vea_encode(
(*self.raw_ptr).ctx,
input_buffer_id,
fd,
planes.len(),
planes.as_mut_ptr(),
timestamp,
if force_keyframe { 1 } else { 0 },
)
};
convert_error_code(r)
}
/// Provides a buffer for storing encoded output.
///
/// When the output buffer has been filled, an `EncoderEvent::ProcessedOutputBuffer`
/// event can be read from the event pipe.
///
/// The caller is responsible for passing in a unique value for `output_buffer_id`
/// which can be referenced when the event is received.
///
/// This function takes ownership of `fd`.
pub fn use_output_buffer(
&self,
output_buffer_id: VeaOutputBufferId,
fd: BufferFd,
offset: u32,
size: u32,
) -> Result<()> {
// Safe because `raw_ptr` is valid and libvda's encode API is called properly.
let r = unsafe {
bindings::vea_use_output_buffer((*self.raw_ptr).ctx, output_buffer_id, fd, offset, size)
};
convert_error_code(r)
}
/// Requests encoding parameter changes.
///
/// The request is not guaranteed to be honored by libvda and could be ignored
/// by the backing encoder implementation.
pub fn request_encoding_params_change(&self, bitrate: u32, framerate: u32) -> Result<()> {
// Safe because `raw_ptr` is valid and libvda's encode API is called properly.
let r = unsafe {
bindings::vea_request_encoding_params_change((*self.raw_ptr).ctx, bitrate, framerate)
};
convert_error_code(r)
}
/// Flushes the encode session.
///
/// When this operation has completed, Event::FlushResponse can be read from
/// the event pipe.
pub fn flush(&self) -> Result<()> {
// Safe because `raw_ptr` is valid and libvda's encode API is called properly.
let r = unsafe { bindings::vea_flush((*self.raw_ptr).ctx) };
convert_error_code(r)
}
}
impl<'a> Drop for Session<'a> {
fn drop(&mut self) {
// Safe because `vea_ptr` and `raw_ptr` are unchanged from the time `new` was called.
// Also, `vea_ptr` is valid because `phantom` guarantees that `VeaInstance` owning `vea_ptr`
// has not dropped yet.
unsafe {
bindings::close_encode_session(self.vea_ptr, self.raw_ptr);
}
}
}