summaryrefslogtreecommitdiff
path: root/src/video/SyncVideoDecoder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/video/SyncVideoDecoder.cpp')
-rw-r--r--src/video/SyncVideoDecoder.cpp261
1 files changed, 261 insertions, 0 deletions
diff --git a/src/video/SyncVideoDecoder.cpp b/src/video/SyncVideoDecoder.cpp
new file mode 100644
index 0000000..a0c62ce
--- /dev/null
+++ b/src/video/SyncVideoDecoder.cpp
@@ -0,0 +1,261 @@
+//
+// libavg - Media Playback Engine.
+// Copyright (C) 2003-2014 Ulrich von Zadow
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library 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
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Current versions can be found at www.libavg.de
+//
+
+#include "SyncVideoDecoder.h"
+#include "FFMpegDemuxer.h"
+
+#include "../base/Exception.h"
+#include "../base/Logger.h"
+#include "../base/ScopeTimer.h"
+#include "../base/ObjectCounter.h"
+#include "../base/ProfilingZoneID.h"
+#include "../base/StringHelper.h"
+
+#include "../graphics/BitmapLoader.h"
+
+#include <iostream>
+#include <sstream>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+using namespace std;
+
+namespace avg {
+
+SyncVideoDecoder::SyncVideoDecoder()
+ : m_pDemuxer(0),
+ m_bFirstPacket(false),
+ m_bUseStreamFPS(true),
+ m_FPS(0)
+{
+ ObjectCounter::get()->incRef(&typeid(*this));
+}
+
+SyncVideoDecoder::~SyncVideoDecoder()
+{
+ ObjectCounter::get()->decRef(&typeid(*this));
+}
+
+void SyncVideoDecoder::open(const string& sFilename, bool bUseHardwareAcceleration,
+ bool bEnableSound)
+{
+ m_bProcessingLastFrames = false;
+ VideoDecoder::open(sFilename, false, false);
+
+ if (getVStreamIndex() >= 0) {
+ if (m_bUseStreamFPS) {
+ m_FPS = getStreamFPS();
+ }
+ m_bFirstPacket = true;
+ m_bVideoSeekDone = false;
+ }
+}
+
+void SyncVideoDecoder::startDecoding(bool bDeliverYCbCr, const AudioParams* pAP)
+{
+ VideoDecoder::startDecoding(bDeliverYCbCr, 0);
+
+ AVG_ASSERT(!m_pDemuxer);
+ vector<int> streamIndexes;
+ streamIndexes.push_back(getVStreamIndex());
+ m_pDemuxer = new FFMpegDemuxer(getFormatContext(), streamIndexes);
+
+ m_pFrameDecoder = FFMpegFrameDecoderPtr(new FFMpegFrameDecoder(getVideoStream()));
+ m_pFrameDecoder->setFPS(m_FPS);
+#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(54, 28, 0)
+ m_pFrame = avcodec_alloc_frame();
+#else
+ m_pFrame = new AVFrame;
+#endif
+}
+
+void SyncVideoDecoder::close()
+{
+ delete m_pDemuxer;
+ m_pDemuxer = 0;
+
+ m_pFrameDecoder = FFMpegFrameDecoderPtr();
+ VideoDecoder::close();
+#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(54, 28, 0)
+ avcodec_free_frame(&m_pFrame);
+#else
+ delete m_pFrame;
+#endif
+}
+
+void SyncVideoDecoder::seek(float destTime)
+{
+ AVG_ASSERT(getState() == DECODING);
+
+ if (m_bFirstPacket) {
+ readFrame(m_pFrame);
+ }
+ m_pDemuxer->seek(destTime);
+ m_bVideoSeekDone = true;
+ m_pFrameDecoder->handleSeek();
+}
+
+void SyncVideoDecoder::loop()
+{
+ seek(0);
+}
+
+int SyncVideoDecoder::getCurFrame() const
+{
+ return int(getCurTime()*getStreamFPS()+0.49);
+}
+
+int SyncVideoDecoder::getNumFramesQueued() const
+{
+ return 0;
+}
+
+float SyncVideoDecoder::getCurTime() const
+{
+ AVG_ASSERT(getState() != CLOSED);
+ if (m_pFrameDecoder) {
+ return m_pFrameDecoder->getCurTime();
+ } else {
+ return 0;
+ }
+}
+
+float SyncVideoDecoder::getFPS() const
+{
+ AVG_ASSERT(getState() != CLOSED);
+ return m_FPS;
+}
+
+void SyncVideoDecoder::setFPS(float fps)
+{
+ m_bUseStreamFPS = (fps == 0);
+ if (fps == 0) {
+ m_FPS = getStreamFPS();
+ } else {
+ m_FPS = fps;
+ }
+ if (m_pFrameDecoder) {
+ m_pFrameDecoder->setFPS(m_FPS);
+ }
+}
+
+static ProfilingZoneID RenderToBmpProfilingZone("FFMpeg: renderToBmp", true);
+static ProfilingZoneID CopyImageProfilingZone("FFMpeg: copy image", true);
+
+FrameAvailableCode SyncVideoDecoder::renderToBmps(vector<BitmapPtr>& pBmps,
+ float timeWanted)
+{
+ AVG_ASSERT(getState() == DECODING);
+ ScopeTimer timer(RenderToBmpProfilingZone);
+ FrameAvailableCode frameAvailable;
+ if (timeWanted == -1) {
+ readFrame(m_pFrame);
+ frameAvailable = FA_NEW_FRAME;
+ } else {
+ frameAvailable = readFrameForTime(m_pFrame, timeWanted);
+ }
+ if (frameAvailable == FA_USE_LAST_FRAME || isEOF()) {
+ return FA_USE_LAST_FRAME;
+ } else {
+ if (pixelFormatIsPlanar(getPixelFormat())) {
+ ScopeTimer timer(CopyImageProfilingZone);
+ for (unsigned i = 0; i < pBmps.size(); ++i) {
+ m_pFrameDecoder->copyPlaneToBmp(pBmps[i], m_pFrame->data[i],
+ m_pFrame->linesize[i]);
+ }
+ } else {
+ m_pFrameDecoder->convertFrameToBmp(m_pFrame, pBmps[0]);
+ }
+ return FA_NEW_FRAME;
+ }
+}
+
+void SyncVideoDecoder::throwAwayFrame(float timeWanted)
+{
+ AVG_ASSERT(getState() == DECODING);
+ readFrameForTime(m_pFrame, timeWanted);
+}
+
+bool SyncVideoDecoder::isEOF() const
+{
+ AVG_ASSERT(getState() == DECODING);
+ return m_pFrameDecoder->isEOF() && !m_bProcessingLastFrames;
+}
+
+FrameAvailableCode SyncVideoDecoder::readFrameForTime(AVFrame* pFrame, float timeWanted)
+{
+ AVG_ASSERT(getState() == DECODING);
+ float timePerFrame = 1.0f/m_FPS;
+ if (!m_bVideoSeekDone && timeWanted-m_pFrameDecoder->getCurTime() < 0.5f*timePerFrame)
+ {
+ // The last frame is still current. Display it again.
+ return FA_USE_LAST_FRAME;
+ } else {
+ bool bInvalidFrame = true;
+ while (bInvalidFrame && !isEOF()) {
+ readFrame(pFrame);
+ bInvalidFrame = m_pFrameDecoder->getCurTime()-timeWanted < -0.5f*timePerFrame;
+ }
+ }
+ if (m_bVideoSeekDone) {
+ m_bVideoSeekDone = false;
+ }
+ return FA_NEW_FRAME;
+}
+
+static ProfilingZoneID DecodeProfilingZone("FFMpeg: decode", true);
+
+void SyncVideoDecoder::readFrame(AVFrame* pFrame)
+{
+ AVG_ASSERT(getState() == DECODING);
+ ScopeTimer timer(DecodeProfilingZone);
+
+ if (m_bProcessingLastFrames) {
+ // EOF received, but last frames still need to be decoded.
+ bool bGotPicture = m_pFrameDecoder->decodeLastFrame(pFrame);
+ if (!bGotPicture) {
+ m_bProcessingLastFrames = false;
+ }
+ } else {
+ bool bDone = false;
+ while (!bDone) {
+ AVPacket* pPacket = m_pDemuxer->getPacket(getVStreamIndex());
+ m_bFirstPacket = false;
+ bool bGotPicture;
+ if (pPacket) {
+ bGotPicture = m_pFrameDecoder->decodePacket(pPacket, pFrame,
+ m_bVideoSeekDone);
+ } else {
+ bGotPicture = m_pFrameDecoder->decodeLastFrame(pFrame);
+ }
+ if (bGotPicture && m_pFrameDecoder->isEOF()) {
+ m_bProcessingLastFrames = true;
+ }
+ if (bGotPicture || m_pFrameDecoder->isEOF()) {
+ bDone = true;
+ }
+ }
+ }
+}
+
+}
+