aboutsummaryrefslogtreecommitdiffstats
path: root/mediaplugin/src/plugins/media/ffmpeg/ffmpeg_video_decode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mediaplugin/src/plugins/media/ffmpeg/ffmpeg_video_decode.cpp')
-rw-r--r--mediaplugin/src/plugins/media/ffmpeg/ffmpeg_video_decode.cpp662
1 files changed, 0 insertions, 662 deletions
diff --git a/mediaplugin/src/plugins/media/ffmpeg/ffmpeg_video_decode.cpp b/mediaplugin/src/plugins/media/ffmpeg/ffmpeg_video_decode.cpp
deleted file mode 100644
index b3fc77b3..00000000
--- a/mediaplugin/src/plugins/media/ffmpeg/ffmpeg_video_decode.cpp
+++ /dev/null
@@ -1,662 +0,0 @@
-/* UltraStar Deluxe - Karaoke Game
- *
- * UltraStar Deluxe is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- */
-
-/*
- * based on 'An ffmpeg and SDL Tutorial' (http://www.dranger.com/ffmpeg/)
- */
-
-#include "ffmpeg_video_decode.h"
-#include <sstream>
-#include <math.h>
-
-// These are called whenever we allocate a frame buffer.
-// We use this to store the global_pts in a frame at the time it is allocated.
-int CDECL ptsGetBuffer(AVCodecContext *codecCtx, AVFrame *frame) {
- int result = avcodec_default_get_buffer(codecCtx, frame);
- int64_t *videoPktPts = (int64_t*)codecCtx->opaque;
- if (videoPktPts) {
- // Note: we must copy the pts instead of passing a pointer, because the packet
- // (and with it the pts) might change before a frame is returned by av_decode_video.
- int64_t *pts = (int64_t*)av_malloc(sizeof(int64_t));
- *pts = *videoPktPts;
- frame->opaque = pts;
- }
- return result;
-}
-
-void CDECL ptsReleaseBuffer(AVCodecContext *codecCtx, AVFrame *frame) {
- if (frame)
- av_freep(&frame->opaque);
- avcodec_default_release_buffer(codecCtx, frame);
-}
-
-/*
- * TVideoDecoder_FFmpeg
- */
-
-FFmpegVideoDecodeStream::FFmpegVideoDecodeStream() :
- _opened(false),
- _eof(false),
- _loop(false),
- _stream(NULL),
- _streamIndex(-1),
- _formatContext(NULL),
- _codecContext(NULL),
- _codec(NULL),
- _avFrame(NULL),
- _avFrameRGB(NULL),
- _frameBuffer(NULL),
- _frameTexValid(false),
-#ifdef USE_SWSCALE
- _swScaleContext(NULL),
-#endif
- _aspect(0),
- _frameDuration(0),
- _frameTime(0),
- _loopTime(0) {}
-
-FFmpegVideoDecodeStream* FFmpegVideoDecodeStream::open(const IPath &filename) {
- FFmpegVideoDecodeStream *stream = new FFmpegVideoDecodeStream();
- if (!stream->_open(filename)) {
- delete stream;
- return 0;
- }
- return stream;
-}
-
-bool FFmpegVideoDecodeStream::_open(const IPath &filename) {
- std::stringstream ss;
-
- // use custom 'ufile' protocol for UTF-8 support
- int errnum = av_open_input_file(&_formatContext,
- ("ufile:" + filename.toUTF8()).c_str(), NULL, 0, NULL);
- if (errnum != 0) {
- logger.error("Failed to open file '" + filename.toNative() + "' ("
- + ffmpegCore->getErrorString(errnum) + ")",
- "VideoDecodeStream_FFmpeg::Open");
- return false;
- }
-
- // update video info
- if (av_find_stream_info(_formatContext) < 0) {
- logger.error("No stream info found", "VideoPlayback_ffmpeg.Open");
- close();
- return false;
- }
-
- ss.str("");
- ss << "VideoStreamIndex: " << _streamIndex;
- logger.info(ss.str(), "VideoPlayback_ffmpeg.Open");
-
- // find video stream
- int audioStreamIndex;
- ffmpegCore->findStreamIDs(_formatContext, &_streamIndex, &audioStreamIndex);
- if (_streamIndex < 0) {
- logger.error("No video stream found", "VideoPlayback_ffmpeg.Open");
- close();
- return false;
- }
-
- _stream = _formatContext->streams[_streamIndex];
- _codecContext = _stream->codec;
-
- _codec = avcodec_find_decoder(_codecContext->codec_id);
- if (!_codec) {
- logger.error("No matching codec found", "VideoPlayback_ffmpeg.Open");
- close();
- return false;
- }
-
- // set debug options
- _codecContext->debug_mv = 0;
- _codecContext->debug = 0;
-
- // detect bug-workarounds automatically
- _codecContext->workaround_bugs = FF_BUG_AUTODETECT;
- // error resilience strategy (careful/compliant/agressive/very_aggressive)
- //fCodecContext->error_resilience = FF_ER_CAREFUL; //FF_ER_COMPLIANT;
- // allow non spec compliant speedup tricks.
- //fCodecContext->flags2 = fCodecContext->flags2 | CODEC_FLAG2_FAST;
-
- // Note: avcodec_open() and avcodec_close() are not thread-safe and will
- // fail if called concurrently by different threads.
- {
- MediaCore_FFmpeg::AVCodecLock codecLock;
- errnum = avcodec_open(_codecContext, _codec);
- }
- if (errnum < 0) {
- logger.error("No matching codec found", "VideoPlayback_ffmpeg.Open");
- close();
- return false;
- }
-
- // register custom callbacks for pts-determination
- _codecContext->get_buffer = ptsGetBuffer;
- _codecContext->release_buffer = ptsReleaseBuffer;
-
-#ifdef DEBUG_DISPLAY
- ss.str("");
- ss << "Found a matching Codec: " << _codecContext->codec->name << std::endl
- << std::endl
- << " Width = " << _codecContext->width
- << ", Height=" << _codecContext->height << std::endl
- << " Aspect : " << _codecContext->sample_aspect_ratio.num << "/"
- << _codecContext->sample_aspect_ratio.den << std::endl
- << " Framerate : " << _codecContext->time_base.num << "/"
- << _codecContext->time_base.den;
- logger.status(ss.str(), "");
-#endif
-
- // allocate space for decoded frame and rgb frame
- _avFrame = avcodec_alloc_frame();
- _avFrameRGB = avcodec_alloc_frame();
- _frameBuffer = (uint8_t*) av_malloc(avpicture_get_size(PIXEL_FMT_FFMPEG,
- _codecContext->width, _codecContext->height));
-
- if (!_avFrame || !_avFrameRGB || !_frameBuffer) {
- logger.error("Failed to allocate buffers", "VideoPlayback_ffmpeg.Open");
- close();
- return false;
- }
-
- // TODO: pad data for OpenGL to GL_UNPACK_ALIGNMENT
- // (otherwise video will be distorted if width/height is not a multiple of the alignment)
- errnum = avpicture_fill((AVPicture*)_avFrameRGB, _frameBuffer,
- PIXEL_FMT_FFMPEG, _codecContext->width, _codecContext->height);
- if (errnum < 0) {
- logger.error("avpicture_fill failed: " + ffmpegCore->getErrorString(errnum),
- "VideoPlayback_ffmpeg.Open");
- close();
- return false;
- }
-
- // calculate some information for video display
- _aspect = av_q2d(_codecContext->sample_aspect_ratio);
- if (_aspect == 0) {
- _aspect = (double)_codecContext->width / _codecContext->height;
- } else {
- _aspect *= (double)_codecContext->width / _codecContext->height;
- }
-
- _frameDuration = 1.0 / av_q2d(_stream->r_frame_rate);
-
- // hack to get reasonable framerate (for divx and others)
- if (_frameDuration < 0.02) { // 0.02 <-> 50 fps
- _frameDuration = av_q2d(_stream->r_frame_rate);
- while (_frameDuration > 50.0)
- _frameDuration /= 10.0;
- _frameDuration = 1.0 / _frameDuration;
- }
-
- ss.str("");
- ss << "Framerate: " << (int)(1.0 / _frameDuration) << "fps";
- logger.info(ss.str(), "VideoPlayback_ffmpeg.Open");
-
-#ifdef USE_SWSCALE
- // if available get a SWScale-context -> faster than the deprecated img_convert().
- // SWScale has accelerated support for PIX_FMT_RGB32/PIX_FMT_BGR24/PIX_FMT_BGR565/PIX_FMT_BGR555.
- // Note: PIX_FMT_RGB32 is a BGR- and not an RGB-format (maybe a bug)!!!
- // The BGR565-formats (GL_UNSIGNED_SHORT_5_6_5) is way too slow because of its
- // bad OpenGL support. The BGR formats have MMX(2) implementations but no speed-up
- // could be observed in comparison to the RGB versions.
- _swScaleContext = sws_getCachedContext(NULL,
- _codecContext->width, _codecContext->height, _codecContext->pix_fmt,
- _codecContext->width, _codecContext->height, PIXEL_FMT_FFMPEG,
- SWS_FAST_BILINEAR,
- NULL, NULL, NULL);
- if (!_swScaleContext) {
- logger.error("Failed to get swscale context", "VideoPlayback_ffmpeg.Open");
- close();
- return false;
- }
-#endif
-
- _opened = true;
- return true;
-}
-
-void FFmpegVideoDecodeStream::close() {
- if (_frameBuffer)
- av_free(_frameBuffer);
- if (_avFrameRGB)
- av_free(_avFrameRGB);
- if (_avFrame)
- av_free(_avFrame);
-
- _avFrame = NULL;
- _avFrameRGB = NULL;
- _frameBuffer = NULL;
-
- if (_codecContext) {
- // avcodec_close() is not thread-safe
- MediaCore_FFmpeg::AVCodecLock codecLock;
- avcodec_close(_codecContext);
- }
-
- if (_formatContext)
- av_close_input_file(_formatContext);
-
- _codecContext = NULL;
- _formatContext = NULL;
-
- _opened = false;
-}
-
-void FFmpegVideoDecodeStream::synchronizeTime(AVFrame *frame, double &pts) {
- if (pts != 0) {
- // if we have pts, set video clock to it
- _frameTime = pts;
- } else {
- // if we aren't given a pts, set it to the clock
- pts = _frameTime;
- }
- // update the video clock
- double frameDelay = av_q2d(_codecContext->time_base);
- // if we are repeating a frame, adjust clock accordingly
- frameDelay = frameDelay + frame->repeat_pict * (frameDelay * 0.5);
- _frameTime = _frameTime + frameDelay;
-}
-
-/**
- * Decode a new frame from the video stream.
- * The decoded frame is stored in fAVFrame. fFrameTime is updated to the new frame's
- * time.
- * @param pts will be updated to the presentation time of the decoded frame.
- * returns true if a frame could be decoded. False if an error or EOF occured.
- */
-bool FFmpegVideoDecodeStream::decodeFrame() {
- int64_t videoPktPts;
- ByteIOContext *pbIOCtx;
- int errnum;
- AVPacket packet;
- double pts;
-
- if (_eof)
- return false;
-
- // read packets until we have a finished frame (or there are no more packets)
- int frameFinished = 0;
- while (frameFinished == 0) {
- errnum = av_read_frame(_formatContext, &packet);
- if (errnum < 0) {
- // failed to read a frame, check reason
-
-#if LIBAVFORMAT_VERSION_MAJOR >= 52
- pbIOCtx = _formatContext->pb;
-#else
- pbIOCtx = &_formatContext->pb;
-#endif
-
- // check for end-of-file (EOF is not an error)
- if (url_feof(pbIOCtx) != 0) {
- _eof = true;
- return false;
- }
-
- // check for errors
- if (url_ferror(pbIOCtx) != 0) {
- logger.error("Video decoding file error", "TVideoPlayback_FFmpeg.DecodeFrame");
- return false;
- }
-
- // url_feof() does not detect an EOF for some mov-files (e.g. deluxe.mov)
- // so we have to do it this way.
- if ((_formatContext->file_size != 0) &&
- (pbIOCtx->pos >= _formatContext->file_size))
- {
- _eof = true;
- return false;
- }
-
- // error occured, log and exit
- logger.error("Video decoding error", "TVideoPlayback_FFmpeg.DecodeFrame");
- return false;
- }
-
- // if we got a packet from the video stream, then decode it
- if (packet.stream_index == _streamIndex) {
- // save pts to be stored in pFrame in first call of PtsGetBuffer()
- videoPktPts = packet.pts;
- // FIXME: is the pointer valid when it is used?
- _codecContext->opaque = &videoPktPts;
-
- // decode packet
- avcodec_decode_video2(_codecContext, _avFrame, &frameFinished, &packet);
-
- // reset opaque data
- _codecContext->opaque = NULL;
-
- // update pts
- if (packet.dts != (int64_t)AV_NOPTS_VALUE) {
- pts = packet.dts;
- } else if (_avFrame->opaque &&
- (*((int64_t*)_avFrame->opaque) != (int64_t)AV_NOPTS_VALUE))
- {
- pts = *((int64_t*)_avFrame->opaque);
- } else {
- pts = 0;
- }
-
- if (_stream->start_time != (int64_t)AV_NOPTS_VALUE)
- pts -= _stream->start_time;
-
- pts *= av_q2d(_stream->time_base);
-
- // synchronize time on each complete frame
- if (frameFinished != 0)
- synchronizeTime(_avFrame, pts);
- }
-
- // free the packet from av_read_frame
- av_free_packet(&packet);
- }
-
- return true;
-}
-
-#ifdef DEBUG_FRAMES
-void spawnGoldenRec(double x, double y, int screen, uint8_t live, int startFrame,
- int recArrayIndex, unsigned player)
-{
- //GoldenRec.Spawn(x, y, screen, live, startFrame, recArrayIndex, ColoredStar, player)
-}
-#endif
-
-uint8_t* FFmpegVideoDecodeStream::getFrame(long double time) {
- const long double SKIP_FRAME_DIFF = 0.010; // start skipping if we are >= 10ms too late
- std::stringstream ss;
-
- if (!_opened)
- return NULL;
-
- /*
- * Synchronization - begin
- */
-
- // requested stream position (relative to the last loop's start)
- long double currentTime;
- if (_loop)
- currentTime = time - _loopTime;
- else
- currentTime = time;
-
- // check if current texture still contains the active frame
- if (_frameTexValid) {
- // time since the last frame was returned
- long double timeDiff = currentTime - _frameTime;
-
-#ifdef DEBUG_DISPLAY
- ss.str("");
- ss << "time: " << floor(time*1000) << std::endl
- << "VideoTime: " << floor(_frameTime*1000) << std::endl
- << "TimeBase: " << floor(_frameDuration*1000) << std::endl
- << "timeDiff: " << floor(timeDiff*1000);
- logger.status(ss.str(), "");
-#endif
-
- // check if time has reached the next frame
- if (timeDiff < _frameDuration) {
-#ifdef DEBUG_FRAMES
- // frame delay debug display
- spawnGoldenRec(200, 15, 1, 16, 0, -1, 0x00ff00);
-#endif
-
-#ifdef DEBUG_DISPLAY
- ss.str("");
- ss << "not getting new frame" << std::endl
- << "time: " << floor(time*1000) << std::endl
- << "VideoTime: " << floor(_frameTime*1000) << std::endl
- << "TimeBase: " << floor(_frameDuration*1000) << std::endl
- << "timeDiff: " << floor(timeDiff*1000);
- logger.status(ss.str(), "");
-#endif
-
- // we do not need a new frame now
- return NULL;
- }
- }
-
- // fetch new frame (updates fFrameTime)
- bool success = decodeFrame();
- long double timeDiff = currentTime - _frameTime;
-
- // check if we have to skip frames
- // Either if we are one frame behind or if the skip threshold has been reached.
- // Do not skip if the difference is less than fFrameDuration as there is no next frame.
- // Note: We assume that fFrameDuration is the length of one frame.
- if (timeDiff >= std::max(_frameDuration, SKIP_FRAME_DIFF)) {
-#ifdef DEBUG_FRAMES
- //frame drop debug display
- spawnGoldenRec(200, 55, 1, 16, 0, -1, 0xff0000);
-#endif
-#ifdef DEBUG_DISPLAY
- ss.str("");
- ss << "skipping frames" << std::endl
- << "TimeBase: " << floor(_frameDuration*1000) << std::endl
- << "timeDiff: " << floor(timeDiff*1000);
- logger.status(ss.str(), "");
-#endif
-
- // update video-time
- int dropFrameCount = (int)(timeDiff / _frameDuration);
- _frameTime = _frameTime + dropFrameCount * _frameDuration;
-
- // skip frames
- for (int i = 1; i <= dropFrameCount; ++i)
- success = decodeFrame();
- }
-
- // check if we got an EOF or error
- if (!success) {
- if (_loop) {
- // we have to loop, so rewind
- setPosition(0);
- // record the start-time of the current loop, so we can
- // determine the position in the stream (fFrameTime-fLoopTime) later.
- _loopTime = time;
- }
- return NULL;
- }
-
- /*
- * Synchronization - end
- */
-
- // TODO: support for pan&scan
- //if (_avFrame->pan_scan) {
- // printf("PanScan: %d/%d", _avFrame->pan_scan->width, _avFrame->pan_scan->height);
- //}
-
- // otherwise we convert the pixeldata from YUV to RGB
- int errnum;
-#ifdef USE_SWSCALE
- errnum = sws_scale(_swScaleContext,
- (uint8_t**)_avFrame->data, _avFrame->linesize,
- 0, _codecContext->height,
- (uint8_t**)_avFrameRGB->data, _avFrameRGB->linesize);
-#else
- // img_convert from lib/ffmpeg/avcodec.pas is actually deprecated.
- // If ./configure does not find SWScale then this gives the error
- // that the identifier img_convert is not known or similar.
- // I think this should be removed, but am not sure whether there should
- // be some other replacement or a warning, Therefore, I leave it for now.
- // April 2009, mischi
- errnum = img_convert((AVPicture*)_avFrameRGB, PIXEL_FMT_FFMPEG,
- (AVPicture*)_avFrame, _codecContext->pix_fmt,
- _codecContext->width, _codecContext->height);
-#endif
-
- if (errnum < 0) {
- logger.error("Image conversion failed", "TVideoPlayback_ffmpeg.GetFrame");
- return NULL;
- }
-
- if (!_frameTexValid)
- _frameTexValid = true;
-
- return _avFrameRGB->data[0];
-}
-
-void FFmpegVideoDecodeStream::setLoop(bool enable) {
- _loop = enable;
- _loopTime = 0;
-}
-
-bool FFmpegVideoDecodeStream::getLoop() {
- return _loop;
-}
-
-/**
- * Sets the stream's position.
- * The stream is set to the first keyframe with timestamp <= Time.
- * Note that fFrameTime is set to Time no matter if the actual position seeked to is
- * at Time or the time of a preceding keyframe. fFrameTime will be updated to the
- * actual frame time when GetFrame() is called the next time.
- * @param Time new position in seconds
- */
-void FFmpegVideoDecodeStream::setPosition(double time) {
- int seekFlags;
-
- if (!_opened)
- return;
-
- if (time < 0)
- time = 0;
-
- // TODO: handle fLoop-times
- //time %= videoDuration;
-
- // Do not use the AVSEEK_FLAG_ANY here. It will seek to any frame, even
- // non keyframes (P-/B-frames). It will produce corrupted video frames as
- // FFmpeg does not use the information of the preceding I-frame.
- // The picture might be gray or green until the next keyframe occurs.
- // Instead seek the first keyframe smaller than the requested time
- // (AVSEEK_FLAG_BACKWARD). As this can be some seconds earlier than the
- // requested time, let the sync in GetFrame() do its job.
- seekFlags = AVSEEK_FLAG_BACKWARD;
-
- _frameTime = time;
- _eof = false;
- _frameTexValid = false;
-
- if (av_seek_frame(_formatContext, _streamIndex,
- llround(time / av_q2d(_stream->time_base)), seekFlags) < 0)
- {
- logger.error("av_seek_frame() failed", "TVideoPlayback_ffmpeg.SetPosition");
- return;
- }
-
- avcodec_flush_buffers(_codecContext);
-}
-
-double FFmpegVideoDecodeStream::getPosition() {
- return _frameTime;
-}
-
-int FFmpegVideoDecodeStream::getFrameWidth() {
- return _codecContext->width;
-}
-
-int FFmpegVideoDecodeStream::getFrameHeight() {
- return _codecContext->height;
-}
-
-double FFmpegVideoDecodeStream::getFrameAspect() {
- return _aspect;
-}
-
-/************************************
- * C Interface
- ************************************/
-
-#define VideoDecodeStreamObj(ptr) reinterpret_cast<FFmpegVideoDecodeStream*>(ptr)
-
-static BOOL PLUGIN_CALL ffmpegVideoDecoder_init() {
- return TRUE;
-}
-
-static BOOL PLUGIN_CALL ffmpegVideoDecoder_finalize() {
- return TRUE;
-}
-
-static videoDecodeStream_t* PLUGIN_CALL ffmpegVideoDecoder_open(const char *filename) {
- return (videoDecodeStream_t*)FFmpegVideoDecodeStream::open(filename);
-}
-
-static void PLUGIN_CALL ffmpegVideoDecoder_close(videoDecodeStream_t *stream) {
- delete VideoDecodeStreamObj(stream);
-}
-
-static void PLUGIN_CALL ffmpegVideoDecoder_setLoop(videoDecodeStream_t *stream, BOOL enable) {
- VideoDecodeStreamObj(stream)->setLoop(enable);
-}
-
-static BOOL PLUGIN_CALL ffmpegVideoDecoder_getLoop(videoDecodeStream_t *stream) {
- return (BOOL)VideoDecodeStreamObj(stream)->getLoop();
-}
-
-static void PLUGIN_CALL ffmpegVideoDecoder_setPosition(videoDecodeStream_t *stream, double time) {
- VideoDecodeStreamObj(stream)->setPosition(time);
-}
-
-static double PLUGIN_CALL ffmpegVideoDecoder_getPosition(videoDecodeStream_t *stream) {
- return VideoDecodeStreamObj(stream)->getPosition();
-}
-
-static int PLUGIN_CALL ffmpegVideoDecoder_getFrameWidth(videoDecodeStream_t *stream) {
- return VideoDecodeStreamObj(stream)->getFrameWidth();
-}
-
-static int PLUGIN_CALL ffmpegVideoDecoder_getFrameHeight(videoDecodeStream_t *stream) {
- return VideoDecodeStreamObj(stream)->getFrameHeight();
-}
-
-static double PLUGIN_CALL ffmpegVideoDecoder_getFrameAspect(videoDecodeStream_t *stream) {
- return VideoDecodeStreamObj(stream)->getFrameAspect();
-}
-
-static uint8_t* PLUGIN_CALL ffmpegVideoDecoder_getFrame(videoDecodeStream_t *stream, long double time) {
- return VideoDecodeStreamObj(stream)->getFrame(time);
-}
-
-/************************************
- * Module information
- ************************************/
-
-const videoDecoderInfo_t videoDecoderInfo = {
- 80,
- ffmpegVideoDecoder_init,
- ffmpegVideoDecoder_finalize,
- ffmpegVideoDecoder_open,
- ffmpegVideoDecoder_close,
- ffmpegVideoDecoder_setLoop,
- ffmpegVideoDecoder_getLoop,
- ffmpegVideoDecoder_setPosition,
- ffmpegVideoDecoder_getPosition,
- ffmpegVideoDecoder_getFrameWidth,
- ffmpegVideoDecoder_getFrameHeight,
- ffmpegVideoDecoder_getFrameAspect,
- ffmpegVideoDecoder_getFrame
-};