--- /dev/null
+use std::fs::File;
+use nihav_core::frame::*;
+use nihav_core::codecs::*;
+use nihav_core::demuxers::*;
+use nihav_core::muxers::*;
+use nihav_core::scale::*;
+use nihav_core::soundcvt::*;
+
+pub struct DecoderTestParams {
+ pub demuxer: &'static str,
+ pub in_name: &'static str,
+ pub limit: Option<u64>,
+ pub stream_type: StreamType,
+ pub dmx_reg: RegisteredDemuxers,
+ pub dec_reg: RegisteredDecoders,
+}
+
+pub struct EncoderTestParams {
+ pub muxer: &'static str,
+ pub enc_name: &'static str,
+ pub out_name: &'static str,
+ pub mux_reg: RegisteredMuxers,
+ pub enc_reg: RegisteredEncoders,
+}
+
+pub fn test_encoding_to_file(dec_config: &DecoderTestParams, enc_config: &EncoderTestParams, mut enc_params: EncodeParameters) {
+ let dmx_f = dec_config.dmx_reg.find_demuxer(dec_config.demuxer).unwrap();
+ let mut file = File::open(dec_config.in_name).unwrap();
+ let mut fr = FileReader::new_read(&mut file);
+ let mut br = ByteReader::new(&mut fr);
+ let mut dmx = create_demuxer(dmx_f, &mut br).unwrap();
+
+ let in_stream = dmx.get_streams().find(|str| str.get_media_type() == dec_config.stream_type).unwrap();
+ let in_stream_id = in_stream.id;
+ let decfunc = dec_config.dec_reg.find_decoder(in_stream.get_info().get_name()).unwrap();
+ let mut dec = (decfunc)();
+ let mut dsupp = Box::new(NADecoderSupport::new());
+ dec.init(&mut dsupp, in_stream.get_info()).unwrap();
+
+ let mut out_sm = StreamManager::new();
+ enc_params.tb_num = in_stream.tb_num;
+ enc_params.tb_den = in_stream.tb_den;
+
+ if let (NACodecTypeInfo::Video(ref mut vinfo), Some(ref_vinfo)) = (&mut enc_params.format, in_stream.get_info().get_properties().get_video_info()) {
+ if vinfo.width == 0 {
+ vinfo.width = ref_vinfo.width;
+ vinfo.height = ref_vinfo.height;
+ }
+ }
+ let mut dst_chmap = NAChannelMap::new();
+ if let (NACodecTypeInfo::Audio(ref mut ainfo), Some(ref_ainfo)) = (&mut enc_params.format, in_stream.get_info().get_properties().get_audio_info()) {
+ if ainfo.sample_rate == 0 {
+ ainfo.sample_rate = ref_ainfo.sample_rate;
+ }
+ if ainfo.channels == 0 {
+ ainfo.channels = ref_ainfo.channels;
+ }
+ match ainfo.channels {
+ 1 => {
+ dst_chmap.add_channel(NAChannelType::C);
+ },
+ 2 => {
+ dst_chmap.add_channel(NAChannelType::L);
+ dst_chmap.add_channel(NAChannelType::R);
+ },
+ _ => panic!("cannot guess channel map"),
+ }
+ }
+
+ let encfunc = enc_config.enc_reg.find_encoder(enc_config.enc_name).unwrap();
+ let mut encoder = (encfunc)();
+ let out_str = encoder.init(0, enc_params).unwrap();
+ out_sm.add_stream(NAStream::clone(&out_str));
+
+ let mux_f = enc_config.mux_reg.find_muxer(enc_config.muxer).unwrap();
+ let out_name = "assets/test_out/".to_owned() + enc_config.out_name;
+ let file = File::create(&out_name).unwrap();
+ let mut fw = FileWriter::new_write(file);
+ let mut bw = ByteWriter::new(&mut fw);
+ let mut mux = create_muxer(mux_f, out_sm, &mut bw).unwrap();
+
+ let (mut ifmt, dst_vinfo) = if let NACodecTypeInfo::Video(vinfo) = enc_params.format {
+ (ScaleInfo { fmt: vinfo.format, width: vinfo.width, height: vinfo.height },
+ vinfo)
+ } else {
+ (ScaleInfo { fmt: YUV420_FORMAT, width: 2, height: 2 },
+ NAVideoInfo { width: 2, height: 2, format: YUV420_FORMAT, flipped: false })
+ };
+ let ofmt = ifmt;
+ let mut scaler = NAScale::new(ifmt, ofmt).unwrap();
+ let mut cvt_buf = alloc_video_buffer(dst_vinfo, 2).unwrap();
+ loop {
+ let pktres = dmx.get_frame();
+ if let Err(e) = pktres {
+ if e == DemuxerError::EOF { break; }
+ panic!("decoding error");
+ }
+ let pkt = pktres.unwrap();
+ if pkt.get_stream().id != in_stream_id { continue; }
+ let frm = dec.decode(&mut dsupp, &pkt).unwrap();
+ let buf = frm.get_buffer();
+ let cfrm = if let NACodecTypeInfo::Video(_) = enc_params.format {
+ let cur_ifmt = get_scale_fmt_from_pic(&buf);
+ if cur_ifmt != ifmt {
+ ifmt = cur_ifmt;
+ scaler = NAScale::new(ifmt, ofmt).unwrap();
+ }
+ scaler.convert(&buf, &mut cvt_buf).unwrap();
+ NAFrame::new(frm.get_time_information(), frm.frame_type, frm.key, frm.get_info(), cvt_buf.clone())
+ } else if let NACodecTypeInfo::Audio(ref dst_ainfo) = enc_params.format {
+ let cvt_buf = convert_audio_frame(&buf, dst_ainfo, &dst_chmap).unwrap();
+ NAFrame::new(frm.get_time_information(), frm.frame_type, frm.key, frm.get_info(), cvt_buf)
+ } else {
+ panic!("unexpected format");
+ };
+ encoder.encode(&cfrm).unwrap();
+ while let Ok(Some(pkt)) = encoder.get_packet() {
+ mux.mux_frame(pkt).unwrap();
+ }
+ if let Some(maxts) = dec_config.limit {
+ if frm.get_pts().unwrap_or(0) >= maxts {
+ break;
+ }
+ }
+ }
+ encoder.flush().unwrap();
+ while let Ok(Some(pkt)) = encoder.get_packet() {
+ mux.mux_frame(pkt).unwrap();
+ }
+ mux.end().unwrap();
+}
self.decs.iter()
}
}
+
+/// A list specifying general encoding errors.
+#[derive(Debug,Clone,Copy,PartialEq)]
+#[allow(dead_code)]
+pub enum EncoderError {
+ /// No frame was provided.
+ NoFrame,
+ /// Allocation failed.
+ AllocError,
+ /// Operation requires repeating.
+ TryAgain,
+ /// Input format is not supported by codec.
+ FormatError,
+ /// Invalid input parameters were provided.
+ InvalidParameters,
+ /// Feature is not implemented.
+ NotImplemented,
+ /// Some bug in encoder. It should not happen yet it might.
+ Bug,
+}
+
+/// A specialised `Result` type for decoding operations.
+pub type EncoderResult<T> = Result<T, EncoderError>;
+
+impl From<ByteIOError> for EncoderError {
+ fn from(_: ByteIOError) -> Self { EncoderError::Bug }
+}
+
+/// Encoding parameter flag to force constant bitrate mode.
+pub const ENC_MODE_CBR: u64 = 1 << 0;
+/// Encoding parameter flag to force constant framerate mode.
+pub const ENC_MODE_CFR: u64 = 1 << 1;
+
+/// Encoding parameters.
+#[derive(Clone,Copy,PartialEq)]
+pub struct EncodeParameters {
+ /// Input format.
+ pub format: NACodecTypeInfo,
+ /// Time base numerator. Ignored for audio.
+ pub tb_num: u32,
+ /// Time base denominator. Ignored for audio.
+ pub tb_den: u32,
+ /// Bitrate in kilobits per second.
+ pub bitrate: u32,
+ /// A collection of various boolean encoder settings like CBR mode.
+ ///
+ /// See `ENC_MODE_*` constants for available options.
+ pub flags: u64,
+ /// Encoding quality.
+ pub quality: u8,
+}
+
+impl Default for EncodeParameters {
+ fn default() -> EncodeParameters {
+ EncodeParameters {
+ format: NACodecTypeInfo::None,
+ tb_num: 0,
+ tb_den: 0,
+ bitrate: 0,
+ flags: 0,
+ quality: 0,
+ }
+ }
+}
+
+/// Encoder trait.
+///
+/// Overall encoding is more complex than decoding.
+/// There are at least two issues that should be addressed: input format and the need for lookahead.
+///
+/// Some formats (like MPEG-1 ones) have fixed picture dimensions and framerate, or sampling rate.
+/// Some formats accept only pictures with dimensions being multiple of eight or sixteen.
+/// Some audio formats work only with monaural sound.
+/// In order to account for all this user first needs to check whether encoder can handle provided input format as is or some conversion is required.
+/// That is why `NAEncoder` has [`negotiate_format`] function that performs such check and returns what encoder can handle.
+///
+/// Additionally, encoders for complex formats often need several frames lookahead to encode data efficiently, actual frame encoding may take place only when some frames are accumulated.
+/// That is why encoder has two functions, one for queueing frames for encoding and one for obtaining encoded packets when they are available.
+/// In result encoder should first queue a frame for encoding with [`encode`] and then retrieve zero or more encoded packets with [`get_packet`] in a loop.
+///
+/// Overall encoding loop should look like this:
+/// ```ignore
+/// let encoder = ...; // create encoder
+/// let enc_params = encoder.negotiate_format(input_enc_params)?; // negotiate format
+/// let output_stream = encoder.init(stream_no, enc_params)?;
+/// while let Some(frame) = queue.get_frame() {
+/// // convert to the format encoder expects if required
+/// encoder.encode(frame)?;
+/// while let Some(enc_pkt) = encoder.get_packet()? {
+/// // send encoded packet to a muxer for example
+/// }
+/// }
+/// // retrieve the rest of encoded packets
+/// encoder.flush()?;
+/// while let Ok(enc_pkt) = encoder.get_packet()? {
+/// // send encoded packet to a muxer for example
+/// }
+/// ```
+///
+/// [`negotiate_format`]: ./trait.NAEncoder.html#tymethod.negotiate_format
+/// [`encode`]: ./trait.NAEncoder.html#tymethod.encode
+/// [`get_packet`]: ./trait.NAEncoder.html#tymethod.get_packet
+pub trait NAEncoder {
+ /// Tries to negotiate input format.
+ ///
+ /// This function takes input encoding parameters and returns adjusted encoding parameters if input ones make sense.
+ /// If input parameters are empty then the default parameters are returned.
+ ///
+ /// # Example
+ /// ```ignore
+ /// let enc_params = [ EncodeParameters {...}, ..., EncodeParameters::default() ];
+ /// let mut target_params = EncodeParameters::default();
+ /// for params in enc_params.iter() {
+ /// if let Ok(dparams) = encoder.negotiate_format(params) {
+ /// target_params = dparams;
+ /// break;
+ /// }
+ /// }
+ /// // since negotiate_format(EncodeParameters::default()) will return a valid format, target_params should be valid here
+ /// let stream = encoder.init(stream_id, target_params)?;
+ /// // convert input into format defined in target_params, feed to the encoder, ...
+ /// ```
+ fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters>;
+ /// Initialises the encoder.
+ fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef>;
+ /// Takes a single frame for encoding.
+ fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()>;
+ /// Returns encoded packet if available.
+ fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>>;
+ /// Tells encoder to encode all data it currently has.
+ fn flush(&mut self) -> EncoderResult<()>;
+}
+
+/// Encoder information used during creating an encoder for requested codec.
+#[derive(Clone,Copy)]
+pub struct EncoderInfo {
+ /// Short encoder name.
+ pub name: &'static str,
+ /// The function that creates an encoder instance.
+ pub get_encoder: fn () -> Box<dyn NAEncoder + Send>,
+}
+
+/// Structure for registering known encoders.
+///
+/// It is supposed to be filled using `register_all_codecs()` from some encoders crate and then it can be used to create encoders for the requested codecs.
+#[derive(Default)]
+pub struct RegisteredEncoders {
+ encs: Vec<EncoderInfo>,
+}
+
+impl RegisteredEncoders {
+ /// Constructs a new instance of `RegisteredEncoders`.
+ pub fn new() -> Self {
+ Self { encs: Vec::new() }
+ }
+ /// Adds another encoder to the registry.
+ pub fn add_encoder(&mut self, enc: EncoderInfo) {
+ self.encs.push(enc);
+ }
+ /// Searches for the encoder for the provided name and returns a function for creating it on success.
+ pub fn find_encoder(&self, name: &str) -> Option<fn () -> Box<dyn NAEncoder + Send>> {
+ for &enc in self.encs.iter() {
+ if enc.name == name {
+ return Some(enc.get_encoder);
+ }
+ }
+ None
+ }
+ /// Provides an iterator over currently registered encoders.
+ pub fn iter(&self) -> std::slice::Iter<EncoderInfo> {
+ self.encs.iter()
+ }
+}
+