use std::sync::Arc;
use v4l2r::controls::codec::VideoGopSize;
use v4l2r::controls::codec::VideoVP8Profile;
use v4l2r::controls::codec::VideoVPXMaxQp;
use v4l2r::controls::codec::VideoVPXMinQp;
use v4l2r::device::Device;
use crate::backend::v4l2::encoder::CaptureBuffers;
use crate::backend::v4l2::encoder::ControlError;
use crate::backend::v4l2::encoder::EncoderCodec;
use crate::backend::v4l2::encoder::InitializationError;
use crate::backend::v4l2::encoder::OutputBufferHandle;
use crate::backend::v4l2::encoder::V4L2Backend;
use crate::encoder::stateful::StatefulEncoder;
use crate::encoder::vp8::EncoderConfig;
use crate::encoder::vp8::VP8;
use crate::encoder::PredictionStructure;
use crate::encoder::Tunings;
use crate::Fourcc;
use crate::Resolution;
const PIXEL_FORMAT_VP8: v4l2r::PixelFormat = v4l2r::PixelFormat::from_fourcc(b"VP80");
pub type V4L2VP8Backend<Handle, CaptureBufferz> = V4L2Backend<Handle, CaptureBufferz, VP8>;
pub type V4L2StatefulVP8Encoder<Handle, CaptureBufferz> =
StatefulEncoder<Handle, V4L2VP8Backend<Handle, CaptureBufferz>>;
impl<Handle, CaptureBufferz> EncoderCodec for V4L2VP8Backend<Handle, CaptureBufferz>
where
Handle: OutputBufferHandle,
CaptureBufferz: CaptureBuffers,
{
fn apply_tunings(device: &Device, tunings: &Tunings) -> Result<(), ControlError> {
let min_qp = VideoVPXMinQp(tunings.min_quality.clamp(0, 127) as i32);
Self::apply_ctrl(device, "vpx min qp", min_qp)?;
let max_qp = VideoVPXMaxQp(tunings.max_quality.clamp(0, 127) as i32);
Self::apply_ctrl(device, "vpx max qp", max_qp)?;
Ok(())
}
}
impl<Handle, CaptureBufferz> V4L2VP8Backend<Handle, CaptureBufferz>
where
Handle: OutputBufferHandle,
CaptureBufferz: CaptureBuffers,
{
pub fn new(
device: Arc<Device>,
capture_buffers: CaptureBufferz,
config: EncoderConfig,
fourcc: Fourcc,
coded_size: Resolution,
tunings: Tunings,
) -> Result<Self, InitializationError> {
match config.pred_structure {
PredictionStructure::LowDelay { limit } => {
let limit = limit as i32;
Self::apply_ctrl(&device, "gop size", VideoGopSize(limit))?;
}
}
Self::apply_ctrl(&device, "vp8 profile", VideoVP8Profile::Profile0)?;
Self::create(
device,
capture_buffers,
fourcc,
coded_size,
config.resolution,
PIXEL_FORMAT_VP8,
tunings,
)
}
}
impl<Handle, CaptureBufferz> V4L2StatefulVP8Encoder<Handle, CaptureBufferz>
where
Handle: OutputBufferHandle,
CaptureBufferz: CaptureBuffers,
{
pub fn new(
device: Arc<Device>,
capture_buffers: CaptureBufferz,
config: EncoderConfig,
fourcc: Fourcc,
coded_size: Resolution,
tunings: Tunings,
) -> Result<Self, InitializationError> {
Ok(Self::create(
tunings.clone(),
V4L2VP8Backend::new(device, capture_buffers, config, fourcc, coded_size, tunings)?,
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
use std::sync::Arc;
use v4l2r::device::Device;
use v4l2r::device::DeviceConfig;
use crate::backend::v4l2::encoder::find_device_with_capture;
use crate::backend::v4l2::encoder::tests::perform_v4l2_encoder_dmabuf_test;
use crate::backend::v4l2::encoder::tests::perform_v4l2_encoder_mmap_test;
use crate::backend::v4l2::encoder::tests::BoPoolAllocator;
use crate::backend::v4l2::encoder::tests::GbmDevice;
use crate::backend::v4l2::encoder::v4l2_format_to_frame_layout;
use crate::backend::v4l2::encoder::MmapingCapture;
use crate::bitstream_utils::IvfFileHeader;
use crate::bitstream_utils::IvfFrameHeader;
use crate::encoder::simple_encode_loop;
use crate::encoder::tests::userptr_test_frame_generator;
use crate::encoder::RateControl;
use crate::utils::DmabufFrame;
use crate::Resolution;
#[ignore]
#[test]
fn test_v4l2_encoder_userptr() {
const VISIBLE_SIZE: Resolution = Resolution { width: 500, height: 500 };
const CODED_SIZE: Resolution = Resolution { width: 512, height: 512 };
const FRAME_COUNT: u64 = 100;
let _ = env_logger::try_init();
let device = find_device_with_capture(PIXEL_FORMAT_VP8).expect("no VP8 encoder found");
let device = Device::open(&device, DeviceConfig::new().non_blocking_dqbuf()).expect("open");
let device = Arc::new(device);
let mut encoder = V4L2StatefulVP8Encoder::new(
device,
MmapingCapture,
EncoderConfig { resolution: VISIBLE_SIZE, ..Default::default() },
Fourcc::from(b"NM12"),
CODED_SIZE,
Tunings { rate_control: RateControl::ConstantBitrate(400_000), ..Default::default() },
)
.unwrap();
let format: v4l2r::Format = encoder.backend().output_format().unwrap();
let layout = v4l2_format_to_frame_layout(&format);
let mut bitstream = Vec::new();
let file_header = IvfFileHeader::new(
IvfFileHeader::CODEC_VP8,
VISIBLE_SIZE.width as u16,
VISIBLE_SIZE.height as u16,
30,
FRAME_COUNT as u32,
);
file_header.writo_into(&mut bitstream).unwrap();
let buffer_size =
format.plane_fmt.iter().map(|plane| plane.sizeimage).max().unwrap() as usize;
let mut frame_producer = userptr_test_frame_generator(FRAME_COUNT, layout, buffer_size);
simple_encode_loop(&mut encoder, &mut frame_producer, |coded| {
let frame_bitstream = &coded.bitstream[4 + 8..];
let header = IvfFrameHeader {
timestamp: coded.metadata.timestamp,
frame_size: frame_bitstream.len() as u32,
};
header.writo_into(&mut bitstream).unwrap();
bitstream.extend(frame_bitstream);
})
.expect("encode loop");
let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true");
if write_to_file {
use std::io::Write;
let mut out = std::fs::File::create("test_v4l2_encoder_userptr.vp8.ivf").unwrap();
out.write_all(&bitstream).unwrap();
out.flush().unwrap();
}
}
#[ignore]
#[test]
fn test_v4l2_encoder_mmap() {
const VISIBLE_SIZE: Resolution = Resolution { width: 500, height: 500 };
const CODED_SIZE: Resolution = Resolution { width: 512, height: 512 };
const FRAME_COUNT: u64 = 100;
let _ = env_logger::try_init();
let device = find_device_with_capture(PIXEL_FORMAT_VP8).expect("no VP8 encoder found");
let device = Device::open(&device, DeviceConfig::new().non_blocking_dqbuf()).expect("open");
let device = Arc::new(device);
let encoder = V4L2StatefulVP8Encoder::new(
device,
MmapingCapture,
EncoderConfig { resolution: VISIBLE_SIZE, ..Default::default() },
Fourcc::from(b"NM12"),
CODED_SIZE,
Tunings { rate_control: RateControl::ConstantBitrate(2_000_000), ..Default::default() },
)
.unwrap();
let mut bitstream = Vec::new();
let file_header = IvfFileHeader::new(
IvfFileHeader::CODEC_VP8,
VISIBLE_SIZE.width as u16,
VISIBLE_SIZE.height as u16,
30,
FRAME_COUNT as u32,
);
file_header.writo_into(&mut bitstream).unwrap();
perform_v4l2_encoder_mmap_test(FRAME_COUNT, encoder, |coded| {
let frame_bitstream = &coded.bitstream[4 + 8..];
let header = IvfFrameHeader {
timestamp: coded.metadata.timestamp,
frame_size: frame_bitstream.len() as u32,
};
header.writo_into(&mut bitstream).unwrap();
bitstream.extend(frame_bitstream);
});
let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true");
if write_to_file {
use std::io::Write;
let mut out = std::fs::File::create("test_v4l2_encoder_mmap.vp8.ivf").unwrap();
out.write_all(&bitstream).unwrap();
out.flush().unwrap();
}
}
#[ignore]
#[test]
fn test_v4l2_encoder_dmabuf() {
const VISIBLE_SIZE: Resolution = Resolution { width: 500, height: 500 };
const CODED_SIZE: Resolution = Resolution { width: 512, height: 512 };
const FRAME_COUNT: u64 = 100;
let _ = env_logger::try_init();
let device = find_device_with_capture(PIXEL_FORMAT_VP8).expect("no VP8 encoder found");
let device = Device::open(&device, DeviceConfig::new().non_blocking_dqbuf()).expect("open");
let device = Arc::new(device);
let gbm = GbmDevice::open(PathBuf::from("/dev/dri/renderD128"))
.and_then(gbm::Device::new)
.expect("failed to create GBM device");
let gbm = Arc::new(gbm);
let encoder = V4L2StatefulVP8Encoder::<DmabufFrame, _>::new(
device.clone(),
BoPoolAllocator::new(gbm.clone()),
EncoderConfig { resolution: VISIBLE_SIZE, ..Default::default() },
Fourcc::from(b"NV12"),
CODED_SIZE,
Tunings {
framerate: 30,
rate_control: RateControl::ConstantBitrate(400_000),
..Default::default()
},
)
.unwrap();
let mut bitstream = Vec::new();
let file_header = IvfFileHeader::new(
IvfFileHeader::CODEC_VP8,
VISIBLE_SIZE.width as u16,
VISIBLE_SIZE.height as u16,
30,
FRAME_COUNT as u32,
);
file_header.writo_into(&mut bitstream).unwrap();
perform_v4l2_encoder_dmabuf_test(
CODED_SIZE,
VISIBLE_SIZE,
FRAME_COUNT,
gbm,
encoder,
|coded| {
let frame_bitstream = &coded.bitstream[4 + 8..];
let header = IvfFrameHeader {
timestamp: coded.metadata.timestamp,
frame_size: frame_bitstream.len() as u32,
};
header.writo_into(&mut bitstream).unwrap();
bitstream.extend(frame_bitstream);
},
);
let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true");
if write_to_file {
use std::io::Write;
let mut out = std::fs::File::create("test_v4l2_encoder_dmabuf.vp8.ivf").unwrap();
out.write_all(&bitstream).unwrap();
out.flush().unwrap();
}
}
}