diff --git a/core/video_provider_lavc.cpp b/core/video_provider_lavc.cpp index 4880af2c2..ecaf97967 100644 --- a/core/video_provider_lavc.cpp +++ b/core/video_provider_lavc.cpp @@ -49,6 +49,9 @@ LAVCVideoProvider::LAVCVideoProvider(wxString filename, wxString subfilename, do formatContext = NULL; codec = NULL; stream = NULL; + frame = NULL; + buffer = NULL; + bufferSize = 0; vidStream = -1; // Register types @@ -113,6 +116,9 @@ void LAVCVideoProvider::LoadVideo(wxString filename) { // Open codec result = avcodec_open(codecContext,codec); if (result < 0) throw _T("Failed to open video decoder"); + + // Allocate frame + frame = avcodec_alloc_frame(); } // Catch errors @@ -126,8 +132,17 @@ void LAVCVideoProvider::LoadVideo(wxString filename) { /////////////// // Close video void LAVCVideoProvider::Close() { + // Clean buffer + if (buffer) delete buffer; + buffer = NULL; + bufferSize = 0; + + // Clean frame + if (frame) av_free(frame); + frame = NULL; + // Close codec context - if (codec) avcodec_close(codecContext); + if (codec && codecContext) avcodec_close(codecContext); codecContext = NULL; codec = NULL; @@ -137,6 +152,99 @@ void LAVCVideoProvider::Close() { } +////////////////// +// Get next frame +void LAVCVideoProvider::GetNextFrame() { + static AVPacket packet; + static bool firstTime = true; + static int bytesRemaining = 0; + static uint8_t *rawdata; + int decoded; + int frameFinished; + + // First call + if (firstTime) { + firstTime = false; + packet.data = NULL; + } + + // Start processing packets + bool run = true; + while (run) { + // Current packet has more bytes + while (bytesRemaining > 0) { + // Decode + decoded = avcodec_decode_video(codecContext,frame,&frameFinished,rawdata,bytesRemaining); + + // Error? + if (decoded < 0) throw _T("Error reading frame"); + + // Update counts + bytesRemaining -= decoded; + rawdata += decoded; + + // Finished? + if (frameFinished) return; + } + + // Get a packet + do { + // Free packet + if (packet.data != NULL) av_free_packet(&packet); + + // Get new + int result = av_read_packet(formatContext,&packet); + if (result < 0) { + run = false; + break; + } + } while (packet.stream_index != vidStream); + + // Get packet data + if (run) { + bytesRemaining = packet.size; + rawdata = packet.data; + } + } + + // End of last pack + decoded = avcodec_decode_video(codecContext,frame,&frameFinished,rawdata,bytesRemaining); + if (packet.data != NULL) av_free_packet(&packet); + + // Frame finished? + if (!frameFinished) throw _T("Unable to finish decoding frame"); +} + + +/////////////////////////////// +// Convert AVFrame to wxBitmap +wxBitmap LAVCVideoProvider::AVFrameToWX(AVFrame *source) { + // Get sizes + int w = codecContext->width; + int h = codecContext->height; + PixelFormat format = PIX_FMT_RGBA32; + unsigned int size = avpicture_get_size(format,w,h); + + // Prepare buffer + if (!buffer || bufferSize != size) { + if (buffer) delete buffer; + buffer = new uint8_t[size]; + bufferSize = size; + } + + // Allocate RGB32 buffer + AVFrame *frameRGB = avcodec_alloc_frame(); + avpicture_fill((AVPicture*) frameRGB, buffer, format, w, h); + + // Convert to RGB32 + img_convert((AVPicture*) frameRGB, format, (AVPicture*) source, codecContext->pix_fmt, w, h); + + // Convert to wxBitmap + wxBitmap bmp((const char*) frameRGB->data[0],w,h,32); + return bmp; +} + + //////////////// // Refresh subs void LAVCVideoProvider::RefreshSubtitles() { @@ -146,8 +254,14 @@ void LAVCVideoProvider::RefreshSubtitles() { ///////////// // Get frame wxBitmap LAVCVideoProvider::GetFrame(int n) { - wxBitmap frame(GetWidth(),GetHeight()); - return frame; + // Get frame + GetNextFrame(); + + // Bitmap + wxBitmap bmp = AVFrameToWX(frame); + + // Return + return bmp; } diff --git a/core/video_provider_lavc.h b/core/video_provider_lavc.h index ea9fc1015..26fc4a796 100644 --- a/core/video_provider_lavc.h +++ b/core/video_provider_lavc.h @@ -53,10 +53,16 @@ private: AVCodecContext *codecContext; AVStream *stream; AVCodec *codec; + AVFrame *frame; int vidStream; + uint8_t *buffer; + int bufferSize; + + void GetNextFrame(); void LoadVideo(wxString filename); void Close(); + wxBitmap AVFrameToWX(AVFrame *frame); public: LAVCVideoProvider(wxString filename, wxString subfilename, double zoom);