LCOV - app.info - content/media/nsBuiltinDecoderReader.cpp

LCOV - code coverage report
| Current view: | directory - content/media - nsBuiltinDecoderReader.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 150 | 0 | 0.0 %
| Date: | 2012-07-07 | Functions: | 9 | 0 | 0.0 %

           1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
           2                 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
           3                 : /* ***** BEGIN LICENSE BLOCK *****
           4                 :  * Version: ML 1.1/GPL 2.0/LGPL 2.1
           5                 :  *
           6                 :  * The contents of this file are subject to the Mozilla Public License Version
           7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
           8                 :  * the License. You may obtain a copy of the License at
           9                 :  * http://www.mozilla.org/MPL/
          10                 :  *
          11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
          12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
          13                 :  * for the specific language governing rights and limitations under the
          14                 :  * License.
          15                 :  *
          16                 :  * The Original Code is Mozilla code.
          17                 :  *
          18                 :  * The Initial Developer of the Original Code is the Mozilla Foundation.
          19                 :  * Portions created by the Initial Developer are Copyright (C) 2010
          20                 :  * the Initial Developer. All Rights Reserved.
          21                 :  *
          22                 :  * Contributor(s):
          23                 :  *  Chris Double <chris.double@double.co.nz>
          24                 :  *  Chris Pearce <chris@pearce.org.nz>
          25                 :  *
          26                 :  * Alternatively, the contents of this file may be used under the terms of
          27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
          28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
          29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
          30                 :  * of those above. If you wish to allow use of your version of this file only
          31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
          32                 :  * use your version of this file under the terms of the MPL, indicate your
          33                 :  * decision by deleting the provisions above and replace them with the notice
          34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
          35                 :  * the provisions above, a recipient may use your version of this file under
          36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
          37                 :  *
          38                 :  * ***** END LICENSE BLOCK ***** */
          39                 :
          40                 : #include "nsBuiltinDecoder.h"
          41                 : #include "nsBuiltinDecoderReader.h"
          42                 : #include "nsBuiltinDecoderStateMachine.h"
          43                 : #include "VideoUtils.h"
          44                 :
          45                 : #include "mozilla/mozalloc.h"
          46                 : #include "mozilla/StandardInteger.h"
          47                 :
          48                 : using namespace mozilla;
          49                 : using mozilla::layers::ImageContainer;
          50                 : using mozilla::layers::PlanarYCbCrImage;
          51                 :
          52                 : // Verify these values are sane. Once we've checked the frame sizes, we then
          53                 : // can do less integer overflow checking.
          54                 : PR_STATIC_ASSERT(MAX_VIDEO_WIDTH < PlanarYCbCrImage::MAX_DIMENSION);
          55                 : PR_STATIC_ASSERT(MAX_VIDEO_HEIGHT < PlanarYCbCrImage::MAX_DIMENSION);
          56                 : PR_STATIC_ASSERT(PlanarYCbCrImage::MAX_DIMENSION < PR_UINT32_MAX / PlanarYCbCrImage::MAX_DIMENSION);
          57                 :
          58                 : // Un-comment to enable logging of seek bisections.
          59                 : //#define SEEK_LOGGING
          60                 :
          61                 : #ifdef PR_LOGGING
          62                 : extern PRLogModuleInfo* gBuiltinDecoderLog;
          63                 : #define LOG(type, msg) PR_LOG(gBuiltinDecoderLog, type, msg)
          64                 : #ifdef SEEK_LOGGING
          65                 : #define SEEK_LOG(type, msg) PR_LOG(gBuiltinDecoderLog, type, msg)
          66                 : #else
          67                 : #define SEEK_LOG(type, msg)
          68                 : #endif
          69                 : #else
          70                 : #define LOG(type, msg)
          71                 : #define SEEK_LOG(type, msg)
          72                 : #endif
          73                 :
          74                 : static bool
          75               0 : ValidatePlane(const VideoData::YCbCrBuffer::Plane& aPlane)
          76                 : {
          77                 :   return aPlane.mWidth <= PlanarYCbCrImage::MAX_DIMENSION &&
          78                 :          aPlane.mHeight <= PlanarYCbCrImage::MAX_DIMENSION &&
          79                 :          aPlane.mWidth * aPlane.mHeight < MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
          80               0 :          aPlane.mStride > 0;
          81                 : }
          82                 :
          83                 : bool
          84               0 : nsVideoInfo::ValidateVideoRegion(const nsIntSize& aFrame,
          85                 :                                  const nsIntRect& aPicture,
          86                 :                                  const nsIntSize& aDisplay)
          87                 : {
          88                 :   return
          89                 :     aFrame.width <= PlanarYCbCrImage::MAX_DIMENSION &&
          90                 :     aFrame.height <= PlanarYCbCrImage::MAX_DIMENSION &&
          91                 :     aFrame.width * aFrame.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
          92                 :     aFrame.width * aFrame.height != 0 &&
          93                 :     aPicture.width <= PlanarYCbCrImage::MAX_DIMENSION &&
          94                 :     aPicture.x < PlanarYCbCrImage::MAX_DIMENSION &&
          95                 :     aPicture.x + aPicture.width < PlanarYCbCrImage::MAX_DIMENSION &&
          96                 :     aPicture.height <= PlanarYCbCrImage::MAX_DIMENSION &&
          97                 :     aPicture.y < PlanarYCbCrImage::MAX_DIMENSION &&
          98                 :     aPicture.y + aPicture.height < PlanarYCbCrImage::MAX_DIMENSION &&
          99                 :     aPicture.width * aPicture.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
         100                 :     aPicture.width * aPicture.height != 0 &&
         101                 :     aDisplay.width <= PlanarYCbCrImage::MAX_DIMENSION &&
         102                 :     aDisplay.height <= PlanarYCbCrImage::MAX_DIMENSION &&
         103                 :     aDisplay.width * aDisplay.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
         104               0 :     aDisplay.width * aDisplay.height != 0;
         105                 : }
         106                 :
         107               0 : VideoData* VideoData::Create(nsVideoInfo& aInfo,
         108                 :                              ImageContainer* aContainer,
         109                 :                              PRInt64 aOffset,
         110                 :                              PRInt64 aTime,
         111                 :                              PRInt64 aEndTime,
         112                 :                              const YCbCrBuffer& aBuffer,
         113                 :                              bool aKeyframe,
         114                 :                              PRInt64 aTimecode,
         115                 :                              nsIntRect aPicture)
         116                 : {
         117               0 :   if (!aContainer) {
         118               0 :     return nsnull;
         119                 :   }
         120                 :
         121                 :   // The following situation should never happen unless there is a bug
         122                 :   // in the decoder
         123               0 :   if (aBuffer.mPlanes[1].mWidth != aBuffer.mPlanes[2].mWidth ||
         124                 :       aBuffer.mPlanes[1].mHeight != aBuffer.mPlanes[2].mHeight) {
         125               0 :     NS_ERROR("C planes with different sizes");
         126               0 :     return nsnull;
         127                 :   }
         128                 :
         129                 :   // The following situations could be triggered by invalid input
         130               0 :   if (aPicture.width <= 0 || aPicture.height <= 0) {
         131               0 :     NS_WARNING("Empty picture rect");
         132               0 :     return nsnull;
         133                 :   }
         134               0 :   if (!ValidatePlane(aBuffer.mPlanes[0]) || !ValidatePlane(aBuffer.mPlanes[1]) ||
         135               0 :       !ValidatePlane(aBuffer.mPlanes[2])) {
         136               0 :     NS_WARNING("Invalid plane size");
         137               0 :     return nsnull;
         138                 :   }
         139                 :
         140                 :   // Ensure the picture size specified in the headers can be extracted out of
         141                 :   // the frame we've been supplied without indexing out of bounds.
         142               0 :   CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
         143               0 :   CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height);
         144               0 :   if (!xLimit.valid() || xLimit.value() > aBuffer.mPlanes[0].mStride ||
         145               0 :       !yLimit.valid() || yLimit.value() > aBuffer.mPlanes[0].mHeight)
         146                 :   {
         147                 :     // The specified picture dimensions can't be contained inside the video
         148                 :     // frame, we'll stomp memory if we try to copy it. Fail.
         149               0 :     NS_WARNING("Overflowing picture rect");
         150               0 :     return nsnull;
         151                 :   }
         152                 :
         153                 :   nsAutoPtr<VideoData> v(new VideoData(aOffset,
         154                 :                                        aTime,
         155                 :                                        aEndTime,
         156                 :                                        aKeyframe,
         157                 :                                        aTimecode,
         158               0 :                                        aInfo.mDisplay));
         159                 :   // Currently our decoder only knows how to output to PLANAR_YCBCR
         160                 :   // format.
         161               0 :   Image::Format format = Image::PLANAR_YCBCR;
         162               0 :   v->mImage = aContainer->CreateImage(&format, 1);
         163               0 :   if (!v->mImage) {
         164               0 :     return nsnull;
         165                 :   }
         166               0 :   NS_ASSERTION(v->mImage->GetFormat() == Image::PLANAR_YCBCR,
         167                 :                "Wrong format?");
         168               0 :   PlanarYCbCrImage* videoImage = static_cast<PlanarYCbCrImage*>(v->mImage.get());
         169                 :
         170               0 :   PlanarYCbCrImage::Data data;
         171               0 :   data.mYChannel = aBuffer.mPlanes[0].mData;
         172               0 :   data.mYSize = gfxIntSize(aBuffer.mPlanes[0].mWidth, aBuffer.mPlanes[0].mHeight);
         173               0 :   data.mYStride = aBuffer.mPlanes[0].mStride;
         174               0 :   data.mCbChannel = aBuffer.mPlanes[1].mData;
         175               0 :   data.mCrChannel = aBuffer.mPlanes[2].mData;
         176               0 :   data.mCbCrSize = gfxIntSize(aBuffer.mPlanes[1].mWidth, aBuffer.mPlanes[1].mHeight);
         177               0 :   data.mCbCrStride = aBuffer.mPlanes[1].mStride;
         178               0 :   data.mPicX = aPicture.x;
         179               0 :   data.mPicY = aPicture.y;
         180               0 :   data.mPicSize = gfxIntSize(aPicture.width, aPicture.height);
         181               0 :   data.mStereoMode = aInfo.mStereoMode;
         182                 :
         183               0 :   videoImage->SetData(data); // Copies buffer
         184               0 :   return v.forget();
         185                 : }
         186                 :
         187               0 : nsBuiltinDecoderReader::nsBuiltinDecoderReader(nsBuiltinDecoder* aDecoder)
         188               0 :   : mDecoder(aDecoder)
         189                 : {
         190               0 :   MOZ_COUNT_CTOR(nsBuiltinDecoderReader);
         191               0 : }
         192                 :
         193               0 : nsBuiltinDecoderReader::~nsBuiltinDecoderReader()
         194                 : {
         195               0 :   ResetDecode();
         196               0 :   MOZ_COUNT_DTOR(nsBuiltinDecoderReader);
         197               0 : }
         198                 :
         199               0 : nsresult nsBuiltinDecoderReader::ResetDecode()
         200                 : {
         201               0 :   nsresult res = NS_OK;
         202                 :
         203               0 :   mVideoQueue.Reset();
         204               0 :   mAudioQueue.Reset();
         205                 :
         206               0 :   return res;
         207                 : }
         208                 :
         209               0 : VideoData* nsBuiltinDecoderReader::FindStartTime(PRInt64& aOutStartTime)
         210                 : {
         211               0 :   NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
         212                 :                "Should be on state machine or decode thread.");
         213                 :
         214                 :   // Extract the start times of the bitstreams in order to calculate
         215                 :   // the duration.
         216               0 :   PRInt64 videoStartTime = INT64_MAX;
         217               0 :   PRInt64 audioStartTime = INT64_MAX;
         218               0 :   VideoData* videoData = nsnull;
         219                 :
         220               0 :   if (HasVideo()) {
         221                 :     videoData = DecodeToFirstData(&nsBuiltinDecoderReader::DecodeVideoFrame,
         222               0 :                                   mVideoQueue);
         223               0 :     if (videoData) {
         224               0 :       videoStartTime = videoData->mTime;
         225                 :     }
         226                 :   }
         227               0 :   if (HasAudio()) {
         228                 :     AudioData* audioData = DecodeToFirstData(&nsBuiltinDecoderReader::DecodeAudioData,
         229               0 :                                              mAudioQueue);
         230               0 :     if (audioData) {
         231               0 :       audioStartTime = audioData->mTime;
         232                 :     }
         233                 :   }
         234                 :
         235               0 :   PRInt64 startTime = NS_MIN(videoStartTime, audioStartTime);
         236               0 :   if (startTime != INT64_MAX) {
         237               0 :     aOutStartTime = startTime;
         238                 :   }
         239                 :
         240               0 :   return videoData;
         241                 : }
         242                 :
         243                 : template<class Data>
         244                 : Data* nsBuiltinDecoderReader::DecodeToFirstData(DecodeFn aDecodeFn,
         245                 :                                                 MediaQueue<Data>& aQueue)
         246                 : {
         247               0 :   bool eof = false;
         248               0 :   while (!eof && aQueue.GetSize() == 0) {
         249                 :     {
         250               0 :       ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
         251               0 :       if (mDecoder->GetDecodeState() == nsDecoderStateMachine::DECODER_STATE_SHUTDOWN) {
         252               0 :         return nsnull;
         253                 :       }
         254                 :     }
         255               0 :     eof = !(this->*aDecodeFn)();
         256                 :   }
         257               0 :   Data* d = nsnull;
         258               0 :   return (d = aQueue.PeekFront()) ? d : nsnull;
         259                 : }
         260                 :
         261               0 : nsresult nsBuiltinDecoderReader::DecodeToTarget(PRInt64 aTarget)
         262                 : {
         263                 :   // Decode forward to the target frame. Start with video, if we have it.
         264               0 :   if (HasVideo()) {
         265               0 :     bool eof = false;
         266               0 :     PRInt64 startTime = -1;
         267               0 :     nsAutoPtr<VideoData> video;
         268               0 :     while (HasVideo() && !eof) {
         269               0 :       while (mVideoQueue.GetSize() == 0 && !eof) {
         270               0 :         bool skip = false;
         271               0 :         eof = !DecodeVideoFrame(skip, 0);
         272                 :         {
         273               0 :           ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
         274               0 :           if (mDecoder->GetDecodeState() == nsBuiltinDecoderStateMachine::DECODER_STATE_SHUTDOWN) {
         275               0 :             return NS_ERROR_FAILURE;
         276                 :           }
         277                 :         }
         278                 :       }
         279               0 :       if (mVideoQueue.GetSize() == 0) {
         280                 :         // Hit end of file, we want to display the last frame of the video.
         281               0 :         if (video) {
         282               0 :           mVideoQueue.PushFront(video.forget());
         283                 :         }
         284               0 :         break;
         285                 :       }
         286               0 :       video = mVideoQueue.PeekFront();
         287                 :       // If the frame end time is less than the seek target, we won't want
         288                 :       // to display this frame after the seek, so discard it.
         289               0 :       if (video && video->mEndTime <= aTarget) {
         290               0 :         if (startTime == -1) {
         291               0 :           startTime = video->mTime;
         292                 :         }
         293               0 :         mVideoQueue.PopFront();
         294                 :       } else {
         295               0 :         video.forget();
         296               0 :         break;
         297                 :       }
         298                 :     }
         299                 :     {
         300               0 :       ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
         301               0 :       if (mDecoder->GetDecodeState() == nsBuiltinDecoderStateMachine::DECODER_STATE_SHUTDOWN) {
         302               0 :         return NS_ERROR_FAILURE;
         303                 :       }
         304                 :     }
         305               0 :     LOG(PR_LOG_DEBUG, ("First video frame after decode is %lld", startTime));
         306                 :   }
         307                 :
         308               0 :   if (HasAudio()) {
         309                 :     // Decode audio forward to the seek target.
         310               0 :     bool eof = false;
         311               0 :     while (HasAudio() && !eof) {
         312               0 :       while (!eof && mAudioQueue.GetSize() == 0) {
         313               0 :         eof = !DecodeAudioData();
         314                 :         {
         315               0 :           ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
         316               0 :           if (mDecoder->GetDecodeState() == nsBuiltinDecoderStateMachine::DECODER_STATE_SHUTDOWN) {
         317               0 :             return NS_ERROR_FAILURE;
         318                 :           }
         319                 :         }
         320                 :       }
         321               0 :       const AudioData* audio = mAudioQueue.PeekFront();
         322               0 :       if (!audio)
         323               0 :         break;
         324               0 :       CheckedInt64 startFrame = UsecsToFrames(audio->mTime, mInfo.mAudioRate);
         325               0 :       CheckedInt64 targetFrame = UsecsToFrames(aTarget, mInfo.mAudioRate);
         326               0 :       if (!startFrame.valid() || !targetFrame.valid()) {
         327               0 :         return NS_ERROR_FAILURE;
         328                 :       }
         329               0 :       if (startFrame.value() + audio->mFrames <= targetFrame.value()) {
         330                 :         // Our seek target lies after the frames in this AudioData. Pop it
         331                 :         // off the queue, and keep decoding forwards.
         332               0 :         delete mAudioQueue.PopFront();
         333               0 :         audio = nsnull;
         334               0 :         continue;
         335                 :       }
         336               0 :       if (startFrame.value() > targetFrame.value()) {
         337                 :         // The seek target doesn't lie in the audio block just after the last
         338                 :         // audio frames we've seen which were before the seek target. This
         339                 :         // could have been the first audio data we've seen after seek, i.e. the
         340                 :         // seek terminated after the seek target in the audio stream. Just
         341                 :         // abort the audio decode-to-target, the state machine will play
         342                 :         // silence to cover the gap. Typically this happens in poorly muxed
         343                 :         // files.
         344               0 :         NS_WARNING("Audio not synced after seek, maybe a poorly muxed file?");
         345               0 :         break;
         346                 :       }
         347                 :
         348                 :       // The seek target lies somewhere in this AudioData's frames, strip off
         349                 :       // any frames which lie before the seek target, so we'll begin playback
         350                 :       // exactly at the seek target.
         351               0 :       NS_ASSERTION(targetFrame.value() >= startFrame.value(),
         352                 :                    "Target must at or be after data start.");
         353               0 :       NS_ASSERTION(targetFrame.value() < startFrame.value() + audio->mFrames,
         354                 :                    "Data must end after target.");
         355                 :
         356               0 :       PRInt64 framesToPrune = targetFrame.value() - startFrame.value();
         357               0 :       if (framesToPrune > audio->mFrames) {
         358                 :         // We've messed up somehow. Don't try to trim frames, the |frames|
         359                 :         // variable below will overflow.
         360               0 :         NS_WARNING("Can't prune more frames that we have!");
         361               0 :         break;
         362                 :       }
         363               0 :       PRUint32 frames = audio->mFrames - static_cast<PRUint32>(framesToPrune);
         364               0 :       PRUint32 channels = audio->mChannels;
         365               0 :       nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[frames * channels]);
         366               0 :       memcpy(audioData.get(),
         367               0 :              audio->mAudioData.get() + (framesToPrune * channels),
         368               0 :              frames * channels * sizeof(AudioDataValue));
         369               0 :       CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudioRate);
         370               0 :       if (!duration.valid()) {
         371               0 :         return NS_ERROR_FAILURE;
         372                 :       }
         373                 :       nsAutoPtr<AudioData> data(new AudioData(audio->mOffset,
         374                 :                                               aTarget,
         375                 :                                               duration.value(),
         376                 :                                               frames,
         377                 :                                               audioData.forget(),
         378               0 :                                               channels));
         379               0 :       delete mAudioQueue.PopFront();
         380               0 :       mAudioQueue.PushFront(data.forget());
         381                 :       break;
         382                 :     }
         383                 :   }
         384               0 :   return NS_OK;
         385                 : }
         386                 :
         387                 :

Generated by: LCOV version 1.7