Каков наилучший метод рендеринга декодированного кадра?
В следующем коде мне нужно знать, использовал ли я правильный метод для рендеринга кадров, полученных после декодирования и масштабирования. Кроме того, я получаю исключение. Я не знаю, что это за исключение, так как через некоторое время я вижу красный крест вместо изображений.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Runtime.InteropServices; using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; using System.Diagnostics; using FFmpeg.AutoGen; using System.Threading; using System.Threading.Tasks; using System.Drawing.Imaging; namespace player.cs { public partial class Form1 : Form { private bool _bPlaying, _bInfoCtrlsUpdated; private string _path; private string _resolution; private string _timebase; private string _codecId; private Bitmap _bmp; public Form1() { InitializeComponent(); backgroundWorker1.DoWork += backgroundWorker1_DoWork; backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged; _path = "1.mp4"; tbPath.Text = _path; } unsafe void PlayVideoFile(DoWorkEventArgs e) { AVBufferRef* hw_device_ctx; Debug.Assert(FFmpegInvoke.av_hwdevice_ctx_create(&hw_device_ctx, FFmpeg.AutoGen.AVHWDeviceType.AV_HWDEVICE_TYPE_DXVA2, null, null, 0) == 0); AVFormatContext* input_ctx = null; Debug.Assert(FFmpegInvoke.avformat_open_input(&input_ctx, _path, null, null) == 0); Debug.Assert(FFmpegInvoke.avformat_find_stream_info(input_ctx, null) >= 0); AVCodec* decoder = null; int video_stream = FFmpegInvoke.av_find_best_stream(input_ctx, AVMediaType.AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0); Debug.Assert(video_stream >= 0); AVCodecContext* decoder_ctx = null; decoder_ctx = FFmpegInvoke.avcodec_alloc_context3(decoder); Debug.Assert(decoder_ctx != null); AVStream* video = null; video = input_ctx->streams[video_stream]; Debug.Assert(FFmpegInvoke.avcodec_parameters_to_context(decoder_ctx, video->codecpar) >= 0); decoder_ctx->hw_device_ctx = FFmpegInvoke.av_buffer_ref(hw_device_ctx); Debug.Assert(FFmpegInvoke.avcodec_open2(decoder_ctx, decoder, null) >= 0); AVFrame* frame = FFmpegInvoke.av_frame_alloc(); Debug.Assert(frame != null); AVPacket packet; _resolution = string.Format("{0}x{1}", decoder_ctx->width, decoder_ctx->height); _timebase = string.Format("{0}/{1}", decoder_ctx->time_base.num, decoder_ctx->time_base.den); _codecId = decoder_ctx->codec_id.ToString(); AVPixelFormat convertToPixFmt = AVPixelFormat.AV_PIX_FMT_RGBA; int dest_width = pictureBox1.Width, dest_height = pictureBox1.Width; SwsContext* convertContext = FFmpegInvoke.sws_getContext( decoder_ctx->width, decoder_ctx->height, AVPixelFormat.AV_PIX_FMT_YUV420P, dest_width, dest_height, convertToPixFmt, FFmpegInvoke.SWS_FAST_BILINEAR, null, null, null); AVFrame* convertedFrame = FFmpegInvoke.av_frame_alloc(); int convertedFrameAspectBufferSize = FFmpegInvoke.avpicture_get_size(convertToPixFmt, dest_width, dest_height); void* convertedFrameBuffer = FFmpegInvoke.av_malloc((uint)convertedFrameAspectBufferSize); FFmpegInvoke.avpicture_fill((AVPicture*)convertedFrame, (byte*)convertedFrameBuffer, convertToPixFmt, dest_width, dest_height); while (true) { while (true) { backgroundWorker1.ReportProgress((int)(video->cur_dts * 100 / video->duration)); if (backgroundWorker1.CancellationPending) break; int ret = FFmpegInvoke.av_read_frame(input_ctx, &packet); if (ret == FFmpegInvoke.AVERROR_EOF) break; Debug.Assert(ret >= 0); if (video_stream != packet.stream_index) continue; ret = FFmpegInvoke.avcodec_send_packet(decoder_ctx, &packet); Debug.Assert(ret >= 0); ret = FFmpegInvoke.avcodec_receive_frame(decoder_ctx, frame); if (ret < 0) continue; ret = FFmpegInvoke.sws_scale( convertContext, &frame->data_0, frame->linesize, 0, frame->height, &convertedFrame->data_0, convertedFrame->linesize); Debug.Assert(ret >= 0); var bmp = new Bitmap( dest_width, dest_height, convertedFrame->linesize[0], PixelFormat.Format32bppPArgb, new IntPtr(convertedFrame->data_0)); pictureBox1.Image = bmp; } if (backgroundWorker1.CancellationPending) { e.Cancel = true; break; } Debug.Assert(FFmpegInvoke.av_seek_frame(input_ctx, video_stream, 0, FFmpegInvoke.AVSEEK_FLAG_FRAME) == 0); } FFmpegInvoke.av_frame_free(&frame); FFmpegInvoke.av_packet_unref(&packet); FFmpegInvoke.avcodec_free_context(&decoder_ctx); FFmpegInvoke.avformat_close_input(&input_ctx); FFmpegInvoke.av_buffer_unref(&hw_device_ctx); } private void button1_Click(object sender, EventArgs e) { var dlg = new OpenFileDialog(); if (dlg.ShowDialog(this) == DialogResult.OK) _path = dlg.FileName; tbPath.Text = _path; } private void button2_Click(object sender, EventArgs e) { if (_bPlaying) backgroundWorker1.CancelAsync(); else backgroundWorker1.RunWorkerAsync(); button2.Text = _bPlaying ? "Play" : "Pause"; _bPlaying = !_bPlaying; } void Play(DoWorkEventArgs e) { _bInfoCtrlsUpdated = false; PlayVideoFile(e); } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { Play(e); } private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { if (!_bInfoCtrlsUpdated) { tbResolution.Text = _resolution; tbTimebase.Text = _timebase; tbCodecID.Text = _codecId; _bInfoCtrlsUpdated = true; } progressBar1.Value = e.ProgressPercentage; } } }
Что я уже пробовал:
Я пытался LockBits не создавать новый экземпляр Bitmap каждый раз для каждого кадра, но это не сработало. Кроме того, я не знаю, было ли это хорошим решением.