Пример #1
0
        /// <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()}");
        }