blob: a37e8e20094c47296a1fd649c464eff927778629 [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::collections::BTreeMap;
use std::fs::File;
use base::error;
use crate::virtio::video::format::{
find_closest_resolution, Format, FormatDesc, Level, PlaneFormat, Profile,
};
use crate::virtio::video::params::Params;
pub type Result<T> = std::result::Result<T, EncoderError>;
#[derive(Debug)]
pub enum EncoderError {
// Invalid argument.
InvalidArgument,
// Platform failure.
PlatformFailure,
// Implementation specific error.
Implementation(Box<dyn std::error::Error + Send>),
}
impl std::fmt::Display for EncoderError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use self::EncoderError::*;
match self {
InvalidArgument => write!(f, "invalid argument"),
PlatformFailure => write!(f, "platform failure"),
Implementation(e) => write!(f, "implementation error: {}", e),
}
}
}
impl std::error::Error for EncoderError {}
pub type InputBufferId = u32;
pub type OutputBufferId = u32;
#[derive(Debug)]
pub enum EncoderEvent {
RequireInputBuffers {
input_count: u32,
input_frame_width: u32,
input_frame_height: u32,
output_buffer_size: u32,
},
ProcessedInputBuffer {
id: InputBufferId,
},
ProcessedOutputBuffer {
id: OutputBufferId,
bytesused: u32,
keyframe: bool,
timestamp: u64,
},
FlushResponse {
flush_done: bool,
},
NotifyError {
error: EncoderError,
},
}
#[derive(Debug)]
pub struct SessionConfig {
pub src_params: Params,
pub dst_params: Params,
pub dst_profile: Profile,
pub dst_bitrate: u32,
pub dst_h264_level: Option<Level>,
pub frame_rate: u32,
}
#[derive(Debug)]
pub struct VideoFramePlane {
pub offset: usize,
pub stride: usize,
}
pub trait EncoderSession {
/// Encodes the frame provided by `resource`, with planes specified by `plane`.
/// `force_keyframe` forces the frame to be encoded as a keyframe.
/// When the buffer has been successfully processed, a `ProcessedInputBuffer` event will
/// be readable from the event pipe, with the same `InputBufferId` as returned by this
/// function.
/// When the corresponding encoded data is ready, `ProcessedOutputBuffer` events will be
/// readable from the event pipe, with the same timestamp as provided `timestamp`.
fn encode(
&mut self,
resource: File,
planes: &[VideoFramePlane],
timestamp: u64,
force_keyframe: bool,
) -> Result<InputBufferId>;
/// Provides an output buffer `file` to store encoded output, where `offset` and `size`
/// define the region of memory to use.
/// When the buffer has been filled with encoded output, a `ProcessedOutputBuffer` event
/// will be readable from the event pipe, with the same `OutputBufferId` as returned by this
/// function.
fn use_output_buffer(&mut self, file: File, offset: u32, size: u32) -> Result<OutputBufferId>;
/// Requests the encoder to flush. When completed, an `EncoderEvent::FlushResponse` event will
/// be readable from the event pipe.
fn flush(&mut self) -> Result<()>;
/// Requests the encoder to use new encoding parameters provided by `bitrate` and `framerate`.
fn request_encoding_params_change(&mut self, bitrate: u32, framerate: u32) -> Result<()>;
/// Returns the event pipe as a pollable file descriptor. When the file descriptor is
/// readable, an event can be read by `read_event`.
fn event_pipe(&self) -> &File;
/// Performs a blocking read for an encoder event. This function should only be called when
/// the file descriptor returned by `event_pipe` is readable.
fn read_event(&mut self) -> Result<EncoderEvent>;
}
#[derive(Clone)]
pub struct EncoderCapabilities {
pub input_format_descs: Vec<FormatDesc>,
pub output_format_descs: Vec<FormatDesc>,
pub coded_format_profiles: BTreeMap<Format, Vec<Profile>>,
}
impl EncoderCapabilities {
pub fn populate_src_params(
&self,
src_params: &mut Params,
desired_format: Format,
desired_width: u32,
desired_height: u32,
mut stride: u32,
) -> Result<()> {
let format_desc = self
.input_format_descs
.iter()
.find(|&format_desc| format_desc.format == desired_format)
.unwrap_or(
self.input_format_descs
.get(0)
.ok_or(EncoderError::PlatformFailure)?,
);
let (allowed_width, allowed_height) =
find_closest_resolution(&format_desc.frame_formats, desired_width, desired_height);
if stride == 0 {
stride = allowed_width;
}
let plane_formats = match format_desc.format {
Format::NV12 => {
let y_plane = PlaneFormat {
plane_size: stride * allowed_height,
stride,
};
let crcb_plane = PlaneFormat {
plane_size: y_plane.plane_size / 2,
stride,
};
vec![y_plane, crcb_plane]
}
_ => {
return Err(EncoderError::PlatformFailure);
}
};
src_params.frame_width = allowed_width;
src_params.frame_height = allowed_height;
src_params.format = Some(format_desc.format.clone());
src_params.plane_formats = plane_formats;
Ok(())
}
pub fn populate_dst_params(
&self,
dst_params: &mut Params,
desired_format: Format,
buffer_size: u32,
) -> Result<()> {
// TODO(alexlau): Should the first be the default?
let format_desc = self
.output_format_descs
.iter()
.find(move |&format_desc| format_desc.format == desired_format)
.unwrap_or(
self.output_format_descs
.get(0)
.ok_or(EncoderError::PlatformFailure)?,
);
dst_params.format = Some(format_desc.format.clone());
// The requested output buffer size might be adjusted by the encoder to match hardware
// requirements in RequireInputBuffers.
dst_params.plane_formats = vec![PlaneFormat {
plane_size: buffer_size,
stride: 0,
}];
Ok(())
}
pub fn get_profiles(&self, coded_format: &Format) -> Option<&Vec<Profile>> {
self.coded_format_profiles.get(coded_format)
}
pub fn get_default_profile(&self, coded_format: &Format) -> Option<Profile> {
let profiles = self.get_profiles(coded_format)?;
match profiles.get(0) {
None => {
error!("Format {} exists but no available profiles.", coded_format);
None
}
Some(profile) => Some(*profile),
}
}
}
pub trait Encoder {
type Session: EncoderSession;
fn query_capabilities(&self) -> Result<EncoderCapabilities>;
fn start_session(&mut self, config: SessionConfig) -> Result<Self::Session>;
fn stop_session(&mut self, session: Self::Session) -> Result<()>;
}