|
| 1 | +// Copyright 2024 The ChromiumOS Authors |
| 2 | +// Use of this source code is governed by a BSD-style license that can be |
| 3 | +// found in the LICENSE file. |
| 4 | + |
| 5 | +use std::borrow::Cow; |
| 6 | +use std::fs::File; |
| 7 | +use std::io::Read; |
| 8 | +use std::io::Write; |
| 9 | +use std::path::PathBuf; |
| 10 | +use std::str::FromStr; |
| 11 | + |
| 12 | +use argh::FromArgs; |
| 13 | +use cros_codecs::backend::v4l2::decoder::stateless::V4l2StatelessDecoderHandle; |
| 14 | +use cros_codecs::codec::h264::parser::Nalu as H264Nalu; |
| 15 | +use cros_codecs::decoder::stateless::h264::H264; |
| 16 | +use cros_codecs::decoder::stateless::StatelessDecoder; |
| 17 | +use cros_codecs::decoder::stateless::StatelessVideoDecoder; |
| 18 | +use cros_codecs::decoder::BlockingMode; |
| 19 | +use cros_codecs::decoder::DecodedHandle; |
| 20 | +use cros_codecs::multiple_desc_type; |
| 21 | +use cros_codecs::utils::simple_playback_loop; |
| 22 | +use cros_codecs::utils::simple_playback_loop_owned_frames; |
| 23 | +use cros_codecs::utils::DmabufFrame; |
| 24 | +use cros_codecs::utils::NalIterator; |
| 25 | +use cros_codecs::utils::UserPtrFrame; |
| 26 | +use cros_codecs::DecodedFormat; |
| 27 | + |
| 28 | +multiple_desc_type! { |
| 29 | + enum BufferDescriptor { |
| 30 | + Managed(()), |
| 31 | + Dmabuf(DmabufFrame), |
| 32 | + User(UserPtrFrame), |
| 33 | + } |
| 34 | +} |
| 35 | + |
| 36 | +#[derive(Debug, PartialEq, Eq, Copy, Clone)] |
| 37 | +enum EncodedFormat { |
| 38 | + H264, |
| 39 | + H265, |
| 40 | + VP8, |
| 41 | + VP9, |
| 42 | + AV1, |
| 43 | +} |
| 44 | + |
| 45 | +impl FromStr for EncodedFormat { |
| 46 | + type Err = &'static str; |
| 47 | + |
| 48 | + fn from_str(s: &str) -> Result<Self, Self::Err> { |
| 49 | + match s { |
| 50 | + "h264" | "H264" => Ok(EncodedFormat::H264), |
| 51 | + "h265" | "H265" => Ok(EncodedFormat::H265), |
| 52 | + "vp8" | "VP8" => Ok(EncodedFormat::VP8), |
| 53 | + "vp9" | "VP9" => Ok(EncodedFormat::VP9), |
| 54 | + "av1" | "AV1" => Ok(EncodedFormat::AV1), |
| 55 | + _ => Err("unrecognized input format. Valid values: h264, h265, vp8, vp9, av1"), |
| 56 | + } |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +#[derive(Debug, PartialEq, Eq, Copy, Clone)] |
| 61 | +enum FrameMemoryType { |
| 62 | + Managed, |
| 63 | + Prime, |
| 64 | + User, |
| 65 | +} |
| 66 | + |
| 67 | +impl FromStr for FrameMemoryType { |
| 68 | + type Err = &'static str; |
| 69 | + |
| 70 | + fn from_str(s: &str) -> Result<Self, Self::Err> { |
| 71 | + match s { |
| 72 | + "managed" => Ok(FrameMemoryType::Managed), |
| 73 | + "prime" => Ok(FrameMemoryType::Prime), |
| 74 | + "user" => Ok(FrameMemoryType::User), |
| 75 | + _ => Err("unrecognized memory type. Valid values: managed, prime, user"), |
| 76 | + } |
| 77 | + } |
| 78 | +} |
| 79 | + |
| 80 | +/// Simple player using cros-codecs |
| 81 | +#[derive(Debug, FromArgs)] |
| 82 | +struct Args { |
| 83 | + /// input file |
| 84 | + #[argh(positional)] |
| 85 | + input: PathBuf, |
| 86 | + |
| 87 | + /// output file to write the decoded frames to |
| 88 | + #[argh(option)] |
| 89 | + output: Option<PathBuf>, |
| 90 | + |
| 91 | + /// input format to decode from. |
| 92 | + #[argh(option)] |
| 93 | + input_format: EncodedFormat, |
| 94 | + |
| 95 | + //TODO /// pixel format to decode into. Default: i420 |
| 96 | + //TODO #[argh(option, default = "DecodedFormat::I420")] |
| 97 | + //TODO output_format: DecodedFormat, |
| 98 | + /// origin of the memory for decoded buffers (managed, prime or user). Default: managed. |
| 99 | + #[argh(option, default = "FrameMemoryType::Managed")] |
| 100 | + frame_memory: FrameMemoryType, |
| 101 | + |
| 102 | + //TODO /// path to the GBM device to use if frame-memory=prime |
| 103 | + //TODO #[argh(option)] |
| 104 | + //TODO gbm_device: Option<PathBuf>, |
| 105 | + /// whether to decode frames synchronously |
| 106 | + #[argh(switch)] |
| 107 | + synchronous: bool, |
| 108 | + //TODO /// whether to display the MD5 of the decoded stream, and at which granularity (stream or |
| 109 | + //TODO /// frame) |
| 110 | + //TODO #[argh(option)] |
| 111 | + //TODO compute_md5: Option<Md5Computation>, |
| 112 | +} |
| 113 | + |
| 114 | +fn main() { |
| 115 | + env_logger::init(); |
| 116 | + |
| 117 | + let args: Args = argh::from_env(); |
| 118 | + |
| 119 | + let input = { |
| 120 | + let mut buf = Vec::new(); |
| 121 | + File::open(args.input) |
| 122 | + .expect("error opening input file") |
| 123 | + .read_to_end(&mut buf) |
| 124 | + .expect("error reading input file"); |
| 125 | + buf |
| 126 | + }; |
| 127 | + |
| 128 | + let mut output = args |
| 129 | + .output |
| 130 | + .as_ref() |
| 131 | + .map(|p| File::create(p).expect("Failed to create output file")); |
| 132 | + |
| 133 | + let blocking_mode = if args.synchronous { |
| 134 | + todo!() // BlockingMode::Blocking |
| 135 | + } else { |
| 136 | + BlockingMode::NonBlocking |
| 137 | + }; |
| 138 | + |
| 139 | + let (mut decoder, frame_iter) = match args.input_format { |
| 140 | + EncodedFormat::H264 => { |
| 141 | + let frame_iter = Box::new(NalIterator::<H264Nalu>::new(&input)) |
| 142 | + as Box<dyn Iterator<Item = Cow<[u8]>>>; |
| 143 | + |
| 144 | + let decoder = Box::new(StatelessDecoder::<H264, _>::new_v4l2(blocking_mode)) |
| 145 | + as Box<dyn StatelessVideoDecoder<_>>; |
| 146 | + |
| 147 | + (decoder, frame_iter) |
| 148 | + } |
| 149 | + EncodedFormat::VP8 => todo!(), |
| 150 | + EncodedFormat::VP9 => todo!(), |
| 151 | + EncodedFormat::H265 => todo!(), |
| 152 | + EncodedFormat::AV1 => todo!(), |
| 153 | + }; |
| 154 | + |
| 155 | + let mut on_new_frame = |handle: V4l2StatelessDecoderHandle| { |
| 156 | + let picture = handle.dyn_picture(); |
| 157 | + let mut handle = picture.dyn_mappable_handle().unwrap(); |
| 158 | + let buffer_size = handle.image_size(); |
| 159 | + let mut frame_data = vec![0; buffer_size]; |
| 160 | + handle.read(&mut frame_data).unwrap(); |
| 161 | + if let Some(output) = &mut output { |
| 162 | + output |
| 163 | + .write_all(&frame_data) |
| 164 | + .expect("Failed to write output file"); |
| 165 | + } |
| 166 | + }; |
| 167 | + |
| 168 | + simple_playback_loop( |
| 169 | + decoder.as_mut(), |
| 170 | + frame_iter, |
| 171 | + &mut on_new_frame, |
| 172 | + &mut |stream_info, nb_frames| { |
| 173 | + Ok(match args.frame_memory { |
| 174 | + FrameMemoryType::Managed => { |
| 175 | + simple_playback_loop_owned_frames(stream_info, nb_frames)? |
| 176 | + .into_iter() |
| 177 | + .collect() |
| 178 | + } |
| 179 | + FrameMemoryType::Prime => todo!(), |
| 180 | + FrameMemoryType::User => todo!(), |
| 181 | + }) |
| 182 | + }, |
| 183 | + DecodedFormat::NV12, |
| 184 | + blocking_mode, |
| 185 | + ) |
| 186 | + .expect("error during playback loop"); |
| 187 | +} |
0 commit comments