/// <summary> /// Return the next frame of a stream. /// </summary> /// <param name="formatContext"> /// The format context. /// </param> /// <returns> /// Value indicating wheter a frame was received. /// </returns> public bool ReadFrame(AVFormatContext formatContext) { int ret = this.client.ReadFrame(formatContext, this); if (ret == 0) { return(true); } else if (ret == NativeFFmpeg.AVERROR_EOF) { return(false); } else { this.client.ThrowOnAVError(ret, postiveIndicatesSuccess: false); return(false); } }
/// <summary> /// Close an opened input AVFormatContext. Free it and all its contents and set *s to NULL. /// </summary> /// <param name="formatContext"> /// The format context. /// </param> public virtual void CloseFormatContext(AVFormatContext formatContext) { var native = formatContext.NativeObject; ffmpeg.avformat_close_input(&native); }
/// <summary> /// Return the next frame of a stream. /// </summary> /// <param name="context"> /// The context. /// </param> /// <param name="packet"> /// The packet. /// </param> /// <returns> /// 0 if OK, negative on error or end of file. /// </returns> public virtual int ReadFrame(AVFormatContext context, AVPacket packet) { return(ffmpeg.av_read_frame(context.NativeObject, packet.NativeObject)); }
/// <summary> /// Open an input stream and read the header. The codecs are not opened. The stream must be closed with avformat_close_input(). /// </summary> /// <param name="context"> /// Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context). /// May be a pointer to NULL, in which case an AVFormatContext is allocated by this /// function and written into ps. Note that a user-supplied AVFormatContext will /// be freed on failure. /// </param> /// <param name="url"> /// URL of the stream to open. /// </param> /// <param name="fmt"> /// If non-NULL, this parameter forces a specific input format. Otherwise the format is autodetected. /// </param> /// <param name="options"> /// A dictionary filled with AVFormatContext and demuxer-private options.On return /// this parameter will be destroyed and replaced with a dict containing options /// that were not found. May be NULL. /// </param> /// <returns> /// 0 on success, a negative AVERROR on failure. /// </returns> public int avformat_open_input(AVFormatContext context, string?url, NativeAVInputFormat *fmt, AVDictionary **options) { var ctx = context.NativeObject; return(ffmpeg.avformat_open_input(&ctx, url, fmt, options)); }
/// <summary> /// Decodes the current stream and blocks until decoding is done. /// </summary> /// <param name="cancellationToken"> /// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous operation. /// </param> public virtual unsafe void Decode(CancellationToken cancellationToken = default) { if (this.disposed) { throw new ObjectDisposedException(nameof(H264Decoder)); } var readDelegate = new NativeReadPacket(this.Read); var readFunc = new NativeReadPacketFunc() { Pointer = Marshal.GetFunctionPointerForDelegate(readDelegate), }; using (var ioContext = new AVIOContext(this.client, ReadBufferSize, readFunc, null)) using (var formatContext = new AVFormatContext(this.client, ioContext)) { formatContext.OpenInputStream("h264"); var stream = formatContext.GetVideoStream(); using (var codec = new AVCodec(this.client, stream)) using (var frame = new AVFrame(this.client)) using (var packet = new AVPacket(this.client)) { int frameNumber = 0; while (packet.ReadFrame(formatContext)) { this.logger.LogDebug($"Got a frame for stream {packet.StreamIndex}"); if (packet.NativeObject->stream_index != stream.Index) { continue; } this.logger.LogDebug("Sending packet"); codec.SendPacket(packet); int framesInPacket = 0; while (codec.ReceiveFrame(frame)) { this.logger.LogDebug("Receiving frame"); framesInPacket += 1; if (frame.PictureType != NativeAVPictureType.AV_PICTURE_TYPE_NONE) { this.logger.LogDebug($"Got a picture of {frame.Width}x{frame.Height} in color space {frame.Format}"); // decode frame this.FrameBuffer.DecompresFrame( frame.Width, frame.Height, frame.Width, frame.Height, frame.Width * 4, new Span <byte>(frame.NativeObject->data[0], frame.NativeObject->linesize[0]), new Span <byte>(frame.NativeObject->data[1], frame.NativeObject->linesize[1]), new Span <byte>(frame.NativeObject->data[2], frame.NativeObject->linesize[2]), new int[] { frame.NativeObject->linesize[0], frame.NativeObject->linesize[1], frame.NativeObject->linesize[2] }); } } this.logger.LogInformation($"Add {framesInPacket} frames in packet."); frameNumber++; } } } GC.KeepAlive(readDelegate); }