diff options
Diffstat (limited to 'src/video/VideoDecoderThread.cpp')
-rw-r--r-- | src/video/VideoDecoderThread.cpp | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/src/video/VideoDecoderThread.cpp b/src/video/VideoDecoderThread.cpp new file mode 100644 index 0000000..d1c701c --- /dev/null +++ b/src/video/VideoDecoderThread.cpp @@ -0,0 +1,222 @@ +// +// 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 "VideoDecoderThread.h" +#include "FFMpegFrameDecoder.h" + +#include "../base/Logger.h" +#include "../base/Exception.h" +#include "../base/ScopeTimer.h" +#include "../base/TimeSource.h" +#include "../avgconfigwrapper.h" + +struct vdpau_render_state; + +using namespace std; + +namespace avg { + +VideoDecoderThread::VideoDecoderThread(CQueue& cmdQ, VideoMsgQueue& msgQ, + VideoMsgQueue& packetQ, AVStream* pStream, const IntPoint& size, PixelFormat pf, + bool bUseVDPAU) + : WorkerThread<VideoDecoderThread>(string("Video Decoder"), cmdQ, + Logger::category::PROFILE_VIDEO), + m_MsgQ(msgQ), + m_PacketQ(packetQ), + m_pBmpQ(new BitmapQueue()), + m_pHalfBmpQ(new BitmapQueue()), + m_Size(size), + m_PF(pf), + m_bUseVDPAU(bUseVDPAU), + m_bSeekDone(false), + m_bProcessingLastFrames(false) +{ + m_pFrameDecoder = FFMpegFrameDecoderPtr(new FFMpegFrameDecoder(pStream)); +} + +VideoDecoderThread::~VideoDecoderThread() +{ +} + +bool VideoDecoderThread::init() +{ +#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(54, 28, 0) + m_pFrame = avcodec_alloc_frame(); +#else + m_pFrame = new AVFrame; +#endif + return true; +} + +void VideoDecoderThread::deinit() +{ +#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(54, 28, 0) + avcodec_free_frame(&m_pFrame); +#else + delete m_pFrame; +#endif +} + +static ProfilingZoneID DecoderProfilingZone("Video Decoder Thread", true); +static ProfilingZoneID PacketWaitProfilingZone("Video wait for packet", true); + +bool VideoDecoderThread::work() +{ + ScopeTimer timer(DecoderProfilingZone); + if (m_bProcessingLastFrames) { + // EOF received, but last frames still need to be decoded. + handleEOF(); + } else { + // Standard decoding. + VideoMsgPtr pMsg; + { + ScopeTimer timer(PacketWaitProfilingZone); + pMsg = m_PacketQ.pop(true); + } + switch (pMsg->getType()) { + case VideoMsg::PACKET: + decodePacket(pMsg->getPacket()); + break; + case VideoMsg::END_OF_FILE: + handleEOF(); + m_bProcessingLastFrames = true; + break; + case VideoMsg::SEEK_DONE: + handleSeekDone(pMsg); + break; + case VideoMsg::CLOSED: + close(); + break; + default: + pMsg->dump(); + AVG_ASSERT(false); + } + } + ThreadProfiler::get()->reset(); + return true; +} + +void VideoDecoderThread::setFPS(float fps) +{ + m_pFrameDecoder->setFPS(fps); +} + +void VideoDecoderThread::returnFrame(VideoMsgPtr pMsg) +{ + m_pBmpQ->push(pMsg->getFrameBitmap(0)); + if (pixelFormatIsPlanar(m_PF)) { + m_pHalfBmpQ->push(pMsg->getFrameBitmap(1)); + m_pHalfBmpQ->push(pMsg->getFrameBitmap(2)); + if (m_PF == YCbCrA420p) { + m_pBmpQ->push(pMsg->getFrameBitmap(3)); + } + } +} + +void VideoDecoderThread::decodePacket(AVPacket* pPacket) +{ + bool bGotPicture = m_pFrameDecoder->decodePacket(pPacket, m_pFrame, m_bSeekDone); + if (bGotPicture) { + m_bSeekDone = false; + sendFrame(m_pFrame); + } +} + +void VideoDecoderThread::handleEOF() +{ + bool bGotPicture = m_pFrameDecoder->decodeLastFrame(m_pFrame); + if (bGotPicture) { + sendFrame(m_pFrame); + } else { + m_bProcessingLastFrames = false; + VideoMsgPtr pMsg(new VideoMsg()); + pMsg->setEOF(); + pushMsg(pMsg); + } +} + +void VideoDecoderThread::handleSeekDone(VideoMsgPtr pMsg) +{ + m_pFrameDecoder->handleSeek(); + m_bSeekDone = true; + m_MsgQ.clear(); + pushMsg(pMsg); +} + +static ProfilingZoneID CopyImageProfilingZone("Copy image", true); + +void VideoDecoderThread::sendFrame(AVFrame* pFrame) +{ + VideoMsgPtr pMsg(new VideoMsg()); + if (m_bUseVDPAU) { + vdpau_render_state *pRenderState = (vdpau_render_state *)pFrame->data[0]; + pMsg->setVDPAUFrame(pRenderState, m_pFrameDecoder->getCurTime()); + } else { + vector<BitmapPtr> pBmps; + if (pixelFormatIsPlanar(m_PF)) { + ScopeTimer timer(CopyImageProfilingZone); + IntPoint halfSize(m_Size.x/2, m_Size.y/2); + pBmps.push_back(getBmp(m_pBmpQ, m_Size, I8)); + pBmps.push_back(getBmp(m_pHalfBmpQ, halfSize, I8)); + pBmps.push_back(getBmp(m_pHalfBmpQ, halfSize, I8)); + if (m_PF == YCbCrA420p) { + pBmps.push_back(getBmp(m_pBmpQ, m_Size, I8)); + } + for (unsigned i = 0; i < pBmps.size(); ++i) { + m_pFrameDecoder->copyPlaneToBmp(pBmps[i], pFrame->data[i], + pFrame->linesize[i]); + } + } else { + pBmps.push_back(getBmp(m_pBmpQ, m_Size, m_PF)); + m_pFrameDecoder->convertFrameToBmp(pFrame, pBmps[0]); + } + pMsg->setFrame(pBmps, m_pFrameDecoder->getCurTime()); + } + pushMsg(pMsg); +} + +void VideoDecoderThread::close() +{ + m_MsgQ.clear(); + stop(); +} + +BitmapPtr VideoDecoderThread::getBmp(BitmapQueuePtr pBmpQ, const IntPoint& size, + PixelFormat pf) +{ + BitmapPtr pBmp = pBmpQ->pop(false); + if (pBmp) { + AVG_ASSERT (pBmp->getSize() == size && pBmp->getPixelFormat() == pf); + return pBmp; + } else { + return BitmapPtr(new Bitmap(size, pf)); + } +} + +static ProfilingZoneID PushMsgProfilingZone("Push message", true); + +void VideoDecoderThread::pushMsg(VideoMsgPtr pMsg) +{ + ScopeTimer timer(PushMsgProfilingZone); + m_MsgQ.push(pMsg); +} + +} |