From 5eba4ce565da9d9ca7b0ba7d79eb3652b10ada96 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Fri, 22 Jul 2022 11:55:30 +0900 Subject: [PATCH] media: ffmpeg: add pixel format wrapper and iterator For the encoder device, we will need to be able to iterate over the supported pixel formats in order to expose the ones that are supported. Add a type to wrap the native AVPixelFormat as well as an iterator to AvCodec that will let us list all the available pixel formats. BUG=b:239897269 TEST=cargo build --features "video-decoder,ffmpeg" Change-Id: Ia835dc428b221c70178f2cd6634566001f3a9256 Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3782040 Commit-Queue: Alexandre Courbot Commit-Queue: Keiichi Watanabe Tested-by: Alexandre Courbot Reviewed-by: Keiichi Watanabe Reviewed-by: Tatsuyuki Ishi Auto-Submit: Alexandre Courbot --- media/ffmpeg/src/avcodec.rs | 88 +++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/media/ffmpeg/src/avcodec.rs b/media/ffmpeg/src/avcodec.rs index 4bc51ba49e..9021f6dfe4 100644 --- a/media/ffmpeg/src/avcodec.rs +++ b/media/ffmpeg/src/avcodec.rs @@ -91,6 +91,14 @@ impl AvCodec { AvProfileIterator(self.0.profiles) } + /// Returns an iterator over the pixel formats supported by this codec. + /// + /// For a decoder, the returned array will likely be empty. This means that ffmpeg's native + /// pixel format (YUV420) will be used. + pub fn pixel_format_iter(&self) -> AvPixelFormatIterator { + AvPixelFormatIterator(self.0.pix_fmts) + } + /// Obtain a context that can be used to decode using this codec. /// /// `get_buffer`'s first element is an optional function that decides which buffer is used to @@ -203,6 +211,86 @@ impl Iterator for AvProfileIterator { } } +/// Simple wrapper over `AVPixelFormat` that provides helpful methods. +pub struct AvPixelFormat(ffi::AVPixelFormat); + +impl AvPixelFormat { + /// Return the name of this pixel format. + pub fn name(&self) -> &'static str { + const INVALID_FORMAT_STR: &str = "invalid pixel format"; + + // Safe because `av_get_pix_fmt_name` returns either NULL or a valid C string. + let pix_fmt_name = unsafe { ffi::av_get_pix_fmt_name(self.0) }; + // Safe because `pix_fmt_name` is a valid pointer to a C string. + match unsafe { + pix_fmt_name + .as_ref() + .and_then(|s| CStr::from_ptr(s).to_str().ok()) + } { + None => INVALID_FORMAT_STR, + Some(string) => string, + } + } + + /// Return the avcodec profile id, which can be matched against AV_PIX_FMT_*. + /// + /// Note that this is **not** the same as a fourcc. + pub fn pix_fmt(&self) -> ffi::AVPixelFormat { + self.0 + } + + /// Return the fourcc of the pixel format, or a series of zeros if its fourcc is unknown. + pub fn fourcc(&self) -> [u8; 4] { + // Safe because `avcodec_pix_fmt_to_codec_tag` does not take any pointer as input and + // handles any value passed as argument. + unsafe { ffi::avcodec_pix_fmt_to_codec_tag(self.0) }.to_le_bytes() + } +} + +impl Display for AvPixelFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.name()) + } +} + +impl Debug for AvPixelFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let fourcc = self.fourcc(); + f.write_fmt(format_args!( + "{}{}{}{}", + fourcc[0] as char, fourcc[1] as char, fourcc[2] as char, fourcc[3] as char + )) + } +} + +/// Lightweight abstraction over the array of supported pixel formats for a given codec. +pub struct AvPixelFormatIterator(*const ffi::AVPixelFormat); + +impl Iterator for AvPixelFormatIterator { + type Item = AvPixelFormat; + + fn next(&mut self) -> Option { + // Safe because the contract of `AvCodec::new` and `AvCodec::pixel_format_iter` guarantees + // that we have been built from a valid `AVCodec` reference, which `pix_fmts` pointer + // must either be NULL or point to a valid array or `VAPixelFormat`s. + match unsafe { self.0.as_ref() } { + None => None, + Some(&pixfmt) => { + match pixfmt { + // Array of pixel formats is terminated by AV_PIX_FMT_NONE. + ffi::AVPixelFormat_AV_PIX_FMT_NONE => None, + _ => { + // Safe because we have been initialized to a static, valid profiles array + // which is terminated by AV_PIX_FMT_NONE. + self.0 = unsafe { self.0.offset(1) }; + Some(AvPixelFormat(pixfmt)) + } + } + } + } + } +} + /// A codec context from which decoding can be performed. pub struct AvCodecContext(*mut ffi::AVCodecContext);