private readonly Stream stream; // The underlying stream /// <summary> /// Initializes the structure with the appropriate delegates for /// a stream. /// </summary> /// <param name="stream">The stream.</param> public StreamDelegates(Stream stream) { this.stream = stream; this.SetPos32 = new SetStreamPos32Delegate(SetPos32Handler); this.GetPos32 = new GetStreamPos32Delegate(GetPos32Handler); this.SetPos64 = new SetStreamPos64Delegate(SetPos64Handler); this.GetPos64 = new GetStreamPos64Delegate(GetPos64Handler); this.Write = new WriteStreamDelegate(WriteHandler); this.Read = new ReadStreamDelegate(ReadHandler); this.GetLength32 = new GetStreamLength32Delegate(GetLength32Handler); this.SetLength32 = new SetStreamLength32Delegate(SetLength32Handler); this.GetLength64 = new GetStreamLength64Delegate(GetLength64Handler); this.SetLength64 = new SetStreamLength64Delegate(SetLength64Handler); }
private void OpenInput() { formatContext = ffmpeg.avformat_alloc_context(); fixed(AVFormatContext **formatContextPtr = &formatContext) { int error; AVInputFormat *inputFormat = null; if (stream != null) { const int bufferSize = 8192; // with delegates and anonymous functions, we can capture // the local stream variable without passing it as a argument. ReadStreamDelegate readStream = (IntPtr opaque, IntPtr buf, int buf_size) => { byte[] array = new byte[buf_size]; int read = stream.Read(array, 0, buf_size); Marshal.Copy(array, 0, buf, read); return(read); }; SeekStreamDelegate seekStream = (IntPtr opaque, Int64 offset, int whence) => { if (whence == ffmpeg.AVSEEK_SIZE) { return(stream.Length); } if (!stream.CanSeek) { return(-1); } stream.Seek(offset, (SeekOrigin)whence); return(stream.Position); }; // track the delegates, GC might remove them prematurely delegateRefs.Add(readStream); delegateRefs.Add(seekStream); // setup custom stream reader for ffmpeg to use with AVIO context sbyte *readBuffer = (sbyte *)ffmpeg.av_malloc(bufferSize + ffmpeg.FF_INPUT_BUFFER_PADDING_SIZE); avioContext = ffmpeg.avio_alloc_context(readBuffer, bufferSize, 0, null, Marshal.GetFunctionPointerForDelegate(readStream), IntPtr.Zero, Marshal.GetFunctionPointerForDelegate(seekStream)); formatContext->pb = avioContext; formatContext->flags |= ffmpeg.AVFMT_FLAG_CUSTOM_IO; if ((error = ffmpeg.av_probe_input_buffer(formatContext->pb, &inputFormat, path, null, 0, 0)) != 0) { throw new FFmpegException(error); } if (path != null && (inputFormat->flags & ffmpeg.AVFMT_NOFILE) != 0) { // Input format (image2) doesn't support custom AVIOContext, // forcefully clear the flag and hope it works. inputFormat->flags &= ~ffmpeg.AVFMT_NOFILE; //ffmpeg.avio_close(formatContext->pb); } } if ((error = ffmpeg.avformat_open_input(formatContextPtr, path, inputFormat, null)) != 0) { throw new FFmpegException(error, "Failed to open input stream for file '" + path + "'"); } } frame = ffmpeg.av_frame_alloc(); }