/// <summary> /// Initializes a new instance of the <see cref="MediaComponent"/> class. /// </summary> /// <param name="container">The container.</param> /// <param name="streamIndex">Index of the stream.</param> /// <exception cref="System.ArgumentNullException">container</exception> /// <exception cref="System.Exception"></exception> protected MediaComponent(MediaContainer container, int streamIndex) { // NOTE: code largely based on stream_component_open if (container == null) { throw new ArgumentNullException(nameof(container)); } Container = container; CodecContext = ffmpeg.avcodec_alloc_context3(null); StreamIndex = streamIndex; Stream = container.InputContext->streams[StreamIndex]; // Set codec options var setCodecParamsResult = ffmpeg.avcodec_parameters_to_context(CodecContext, Stream->codecpar); if (setCodecParamsResult < 0) { Container.Log(MediaLogMessageType.Warning, $"Could not set codec parameters. Error code: {setCodecParamsResult}"); } // We set the packet timebase in the same timebase as the stream as opposed to the tpyical AV_TIME_BASE ffmpeg.av_codec_set_pkt_timebase(CodecContext, Stream->time_base); // Find the codec and set it. var codec = ffmpeg.avcodec_find_decoder(Stream->codec->codec_id); if (codec == null) { var errorMessage = $"Fatal error. Unable to find suitable decoder for {Stream->codec->codec_id.ToString()}"; Dispose(); throw new MediaContainerException(errorMessage); } CodecContext->codec_id = codec->id; // Process the low res index option var lowResIndex = ffmpeg.av_codec_get_max_lowres(codec); if (Container.MediaOptions.EnableLowRes) { ffmpeg.av_codec_set_lowres(CodecContext, lowResIndex); CodecContext->flags |= ffmpeg.CODEC_FLAG_EMU_EDGE; } else { lowResIndex = 0; } // Configure the codec context flags if (Container.MediaOptions.EnableFastDecoding) { CodecContext->flags2 |= ffmpeg.AV_CODEC_FLAG2_FAST; } if ((codec->capabilities & ffmpeg.AV_CODEC_CAP_DR1) != 0) { CodecContext->flags |= ffmpeg.CODEC_FLAG_EMU_EDGE; } if ((codec->capabilities & ffmpeg.AV_CODEC_CAP_TRUNCATED) != 0) { CodecContext->flags |= ffmpeg.AV_CODEC_CAP_TRUNCATED; } if ((codec->capabilities & ffmpeg.CODEC_FLAG2_CHUNKS) != 0) { CodecContext->flags |= ffmpeg.CODEC_FLAG2_CHUNKS; } // Setup additional settings. The most important one is Threads -- Setting it to 1 decoding is very slow. Setting it to auto // decoding is very fast in most scenarios. var codecOptions = Container.MediaOptions.CodecOptions.FilterOptions(CodecContext->codec_id, Container.InputContext, Stream, codec); if (codecOptions.HasKey(CodecOption.Threads) == false) { codecOptions[CodecOption.Threads] = "auto"; } if (lowResIndex != 0) { codecOptions[CodecOption.LowRes] = lowResIndex.ToString(CultureInfo.InvariantCulture); } if (CodecContext->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO || CodecContext->codec_type == AVMediaType.AVMEDIA_TYPE_AUDIO) { codecOptions[CodecOption.RefCountedFrames] = 1.ToString(CultureInfo.InvariantCulture); } // Open the CodecContext var codecOpenResult = 0; fixed(AVDictionary **reference = &codecOptions.Pointer) codecOpenResult = ffmpeg.avcodec_open2(CodecContext, codec, reference); if (codecOpenResult < 0) { Dispose(); throw new MediaContainerException($"Unable to open codec. Error code {codecOpenResult}"); } // If there are any codec options left over from passing them, it means they were not consumed if (codecOptions.First() != null) { Container.Log(MediaLogMessageType.Warning, $"Codec Option '{codecOptions.First().Key}' not found."); } // Startup done. Set some options. Stream->discard = AVDiscard.AVDISCARD_DEFAULT; MediaType = (MediaType)CodecContext->codec_type; // Compute the start time if (Stream->start_time == Constants.AV_NOPTS) { StartTimeOffset = Container.MediaStartTimeOffset; } else { StartTimeOffset = Stream->start_time.ToTimeSpan(Stream->time_base); } // compute the duration if (Stream->duration == Constants.AV_NOPTS || Stream->duration == 0) { Duration = Container.InputContext->duration.ToTimeSpan(); } else { Duration = Stream->duration.ToTimeSpan(Stream->time_base); } CodecId = Stream->codec->codec_id; CodecName = ffmpeg.avcodec_get_name(CodecId); Bitrate = (int)Stream->codec->bit_rate; Container.Log(MediaLogMessageType.Trace, $"{MediaType}: Start Offset: {StartTimeOffset.Debug()}; Duration: {Duration.Debug()}"); }