content/media/raw/RawReader.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "MediaDecoderStateMachine.h"
michael@0 7 #include "AbstractMediaDecoder.h"
michael@0 8 #include "RawReader.h"
michael@0 9 #include "RawDecoder.h"
michael@0 10 #include "VideoUtils.h"
michael@0 11 #include "nsISeekableStream.h"
michael@0 12 #include "gfx2DGlue.h"
michael@0 13
michael@0 14 using namespace mozilla;
michael@0 15
michael@0 16 RawReader::RawReader(AbstractMediaDecoder* aDecoder)
michael@0 17 : MediaDecoderReader(aDecoder),
michael@0 18 mCurrentFrame(0), mFrameSize(0)
michael@0 19 {
michael@0 20 MOZ_COUNT_CTOR(RawReader);
michael@0 21 }
michael@0 22
michael@0 23 RawReader::~RawReader()
michael@0 24 {
michael@0 25 MOZ_COUNT_DTOR(RawReader);
michael@0 26 }
michael@0 27
michael@0 28 nsresult RawReader::Init(MediaDecoderReader* aCloneDonor)
michael@0 29 {
michael@0 30 return NS_OK;
michael@0 31 }
michael@0 32
michael@0 33 nsresult RawReader::ResetDecode()
michael@0 34 {
michael@0 35 mCurrentFrame = 0;
michael@0 36 return MediaDecoderReader::ResetDecode();
michael@0 37 }
michael@0 38
michael@0 39 nsresult RawReader::ReadMetadata(MediaInfo* aInfo,
michael@0 40 MetadataTags** aTags)
michael@0 41 {
michael@0 42 NS_ASSERTION(mDecoder->OnDecodeThread(),
michael@0 43 "Should be on decode thread.");
michael@0 44
michael@0 45 MediaResource* resource = mDecoder->GetResource();
michael@0 46 NS_ASSERTION(resource, "Decoder has no media resource");
michael@0 47
michael@0 48 if (!ReadFromResource(resource, reinterpret_cast<uint8_t*>(&mMetadata),
michael@0 49 sizeof(mMetadata)))
michael@0 50 return NS_ERROR_FAILURE;
michael@0 51
michael@0 52 // Validate the header
michael@0 53 if (!(mMetadata.headerPacketID == 0 /* Packet ID of 0 for the header*/ &&
michael@0 54 mMetadata.codecID == RAW_ID /* "YUV" */ &&
michael@0 55 mMetadata.majorVersion == 0 &&
michael@0 56 mMetadata.minorVersion == 1))
michael@0 57 return NS_ERROR_FAILURE;
michael@0 58
michael@0 59 CheckedUint32 dummy = CheckedUint32(static_cast<uint32_t>(mMetadata.frameWidth)) *
michael@0 60 static_cast<uint32_t>(mMetadata.frameHeight);
michael@0 61 NS_ENSURE_TRUE(dummy.isValid(), NS_ERROR_FAILURE);
michael@0 62
michael@0 63 if (mMetadata.aspectDenominator == 0 ||
michael@0 64 mMetadata.framerateDenominator == 0)
michael@0 65 return NS_ERROR_FAILURE; // Invalid data
michael@0 66
michael@0 67 // Determine and verify frame display size.
michael@0 68 float pixelAspectRatio = static_cast<float>(mMetadata.aspectNumerator) /
michael@0 69 mMetadata.aspectDenominator;
michael@0 70 nsIntSize display(mMetadata.frameWidth, mMetadata.frameHeight);
michael@0 71 ScaleDisplayByAspectRatio(display, pixelAspectRatio);
michael@0 72 mPicture = nsIntRect(0, 0, mMetadata.frameWidth, mMetadata.frameHeight);
michael@0 73 nsIntSize frameSize(mMetadata.frameWidth, mMetadata.frameHeight);
michael@0 74 if (!IsValidVideoRegion(frameSize, mPicture, display)) {
michael@0 75 // Video track's frame sizes will overflow. Fail.
michael@0 76 return NS_ERROR_FAILURE;
michael@0 77 }
michael@0 78
michael@0 79 mInfo.mVideo.mHasVideo = true;
michael@0 80 mInfo.mVideo.mDisplay = display;
michael@0 81
michael@0 82 mFrameRate = static_cast<float>(mMetadata.framerateNumerator) /
michael@0 83 mMetadata.framerateDenominator;
michael@0 84
michael@0 85 // Make some sanity checks
michael@0 86 if (mFrameRate > 45 ||
michael@0 87 mFrameRate == 0 ||
michael@0 88 pixelAspectRatio == 0 ||
michael@0 89 mMetadata.frameWidth > 2000 ||
michael@0 90 mMetadata.frameHeight > 2000 ||
michael@0 91 mMetadata.chromaChannelBpp != 4 ||
michael@0 92 mMetadata.lumaChannelBpp != 8 ||
michael@0 93 mMetadata.colorspace != 1 /* 4:2:0 */)
michael@0 94 return NS_ERROR_FAILURE;
michael@0 95
michael@0 96 mFrameSize = mMetadata.frameWidth * mMetadata.frameHeight *
michael@0 97 (mMetadata.lumaChannelBpp + mMetadata.chromaChannelBpp) / 8.0 +
michael@0 98 sizeof(RawPacketHeader);
michael@0 99
michael@0 100 int64_t length = resource->GetLength();
michael@0 101 if (length != -1) {
michael@0 102 ReentrantMonitorAutoEnter autoMonitor(mDecoder->GetReentrantMonitor());
michael@0 103 mDecoder->SetMediaDuration(USECS_PER_S *
michael@0 104 (length - sizeof(RawVideoHeader)) /
michael@0 105 (mFrameSize * mFrameRate));
michael@0 106 }
michael@0 107
michael@0 108 *aInfo = mInfo;
michael@0 109
michael@0 110 *aTags = nullptr;
michael@0 111
michael@0 112 return NS_OK;
michael@0 113 }
michael@0 114
michael@0 115 bool RawReader::DecodeAudioData()
michael@0 116 {
michael@0 117 NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
michael@0 118 "Should be on state machine thread or decode thread.");
michael@0 119 return false;
michael@0 120 }
michael@0 121
michael@0 122 // Helper method that either reads until it gets aLength bytes
michael@0 123 // or returns false
michael@0 124 bool RawReader::ReadFromResource(MediaResource *aResource, uint8_t* aBuf,
michael@0 125 uint32_t aLength)
michael@0 126 {
michael@0 127 while (aLength > 0) {
michael@0 128 uint32_t bytesRead = 0;
michael@0 129 nsresult rv;
michael@0 130
michael@0 131 rv = aResource->Read(reinterpret_cast<char*>(aBuf), aLength, &bytesRead);
michael@0 132 NS_ENSURE_SUCCESS(rv, false);
michael@0 133
michael@0 134 if (bytesRead == 0) {
michael@0 135 return false;
michael@0 136 }
michael@0 137
michael@0 138 aLength -= bytesRead;
michael@0 139 aBuf += bytesRead;
michael@0 140 }
michael@0 141
michael@0 142 return true;
michael@0 143 }
michael@0 144
michael@0 145 bool RawReader::DecodeVideoFrame(bool &aKeyframeSkip,
michael@0 146 int64_t aTimeThreshold)
michael@0 147 {
michael@0 148 NS_ASSERTION(mDecoder->OnDecodeThread(),
michael@0 149 "Should be on decode thread.");
michael@0 150
michael@0 151 // Record number of frames decoded and parsed. Automatically update the
michael@0 152 // stats counters using the AutoNotifyDecoded stack-based class.
michael@0 153 uint32_t parsed = 0, decoded = 0;
michael@0 154 AbstractMediaDecoder::AutoNotifyDecoded autoNotify(mDecoder, parsed, decoded);
michael@0 155
michael@0 156 if (!mFrameSize)
michael@0 157 return false; // Metadata read failed. We should refuse to play.
michael@0 158
michael@0 159 int64_t currentFrameTime = USECS_PER_S * mCurrentFrame / mFrameRate;
michael@0 160 uint32_t length = mFrameSize - sizeof(RawPacketHeader);
michael@0 161
michael@0 162 nsAutoArrayPtr<uint8_t> buffer(new uint8_t[length]);
michael@0 163 MediaResource* resource = mDecoder->GetResource();
michael@0 164 NS_ASSERTION(resource, "Decoder has no media resource");
michael@0 165
michael@0 166 // We're always decoding one frame when called
michael@0 167 while(true) {
michael@0 168 RawPacketHeader header;
michael@0 169
michael@0 170 // Read in a packet header and validate
michael@0 171 if (!(ReadFromResource(resource, reinterpret_cast<uint8_t*>(&header),
michael@0 172 sizeof(header))) ||
michael@0 173 !(header.packetID == 0xFF && header.codecID == RAW_ID /* "YUV" */)) {
michael@0 174 return false;
michael@0 175 }
michael@0 176
michael@0 177 if (!ReadFromResource(resource, buffer, length)) {
michael@0 178 return false;
michael@0 179 }
michael@0 180
michael@0 181 parsed++;
michael@0 182
michael@0 183 if (currentFrameTime >= aTimeThreshold)
michael@0 184 break;
michael@0 185
michael@0 186 mCurrentFrame++;
michael@0 187 currentFrameTime += static_cast<double>(USECS_PER_S) / mFrameRate;
michael@0 188 }
michael@0 189
michael@0 190 VideoData::YCbCrBuffer b;
michael@0 191 b.mPlanes[0].mData = buffer;
michael@0 192 b.mPlanes[0].mStride = mMetadata.frameWidth * mMetadata.lumaChannelBpp / 8.0;
michael@0 193 b.mPlanes[0].mHeight = mMetadata.frameHeight;
michael@0 194 b.mPlanes[0].mWidth = mMetadata.frameWidth;
michael@0 195 b.mPlanes[0].mOffset = b.mPlanes[0].mSkip = 0;
michael@0 196
michael@0 197 uint32_t cbcrStride = mMetadata.frameWidth * mMetadata.chromaChannelBpp / 8.0;
michael@0 198
michael@0 199 b.mPlanes[1].mData = buffer + mMetadata.frameHeight * b.mPlanes[0].mStride;
michael@0 200 b.mPlanes[1].mStride = cbcrStride;
michael@0 201 b.mPlanes[1].mHeight = mMetadata.frameHeight / 2;
michael@0 202 b.mPlanes[1].mWidth = mMetadata.frameWidth / 2;
michael@0 203 b.mPlanes[1].mOffset = b.mPlanes[1].mSkip = 0;
michael@0 204
michael@0 205 b.mPlanes[2].mData = b.mPlanes[1].mData + mMetadata.frameHeight * cbcrStride / 2;
michael@0 206 b.mPlanes[2].mStride = cbcrStride;
michael@0 207 b.mPlanes[2].mHeight = mMetadata.frameHeight / 2;
michael@0 208 b.mPlanes[2].mWidth = mMetadata.frameWidth / 2;
michael@0 209 b.mPlanes[2].mOffset = b.mPlanes[2].mSkip = 0;
michael@0 210
michael@0 211 VideoData *v = VideoData::Create(mInfo.mVideo,
michael@0 212 mDecoder->GetImageContainer(),
michael@0 213 -1,
michael@0 214 currentFrameTime,
michael@0 215 (USECS_PER_S / mFrameRate),
michael@0 216 b,
michael@0 217 1, // In raw video every frame is a keyframe
michael@0 218 -1,
michael@0 219 ToIntRect(mPicture));
michael@0 220 if (!v)
michael@0 221 return false;
michael@0 222
michael@0 223 mVideoQueue.Push(v);
michael@0 224 mCurrentFrame++;
michael@0 225 decoded++;
michael@0 226 currentFrameTime += USECS_PER_S / mFrameRate;
michael@0 227
michael@0 228 return true;
michael@0 229 }
michael@0 230
michael@0 231 nsresult RawReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime)
michael@0 232 {
michael@0 233 NS_ASSERTION(mDecoder->OnDecodeThread(),
michael@0 234 "Should be on decode thread.");
michael@0 235
michael@0 236 MediaResource *resource = mDecoder->GetResource();
michael@0 237 NS_ASSERTION(resource, "Decoder has no media resource");
michael@0 238
michael@0 239 uint32_t frame = mCurrentFrame;
michael@0 240 if (aTime >= UINT_MAX)
michael@0 241 return NS_ERROR_FAILURE;
michael@0 242 mCurrentFrame = aTime * mFrameRate / USECS_PER_S;
michael@0 243
michael@0 244 CheckedUint32 offset = CheckedUint32(mCurrentFrame) * mFrameSize;
michael@0 245 offset += sizeof(RawVideoHeader);
michael@0 246 NS_ENSURE_TRUE(offset.isValid(), NS_ERROR_FAILURE);
michael@0 247
michael@0 248 nsresult rv = resource->Seek(nsISeekableStream::NS_SEEK_SET, offset.value());
michael@0 249 NS_ENSURE_SUCCESS(rv, rv);
michael@0 250
michael@0 251 mVideoQueue.Reset();
michael@0 252
michael@0 253 while(mVideoQueue.GetSize() == 0) {
michael@0 254 bool keyframeSkip = false;
michael@0 255 if (!DecodeVideoFrame(keyframeSkip, 0)) {
michael@0 256 mCurrentFrame = frame;
michael@0 257 return NS_ERROR_FAILURE;
michael@0 258 }
michael@0 259
michael@0 260 {
michael@0 261 ReentrantMonitorAutoEnter autoMonitor(mDecoder->GetReentrantMonitor());
michael@0 262 if (mDecoder->IsShutdown()) {
michael@0 263 mCurrentFrame = frame;
michael@0 264 return NS_ERROR_FAILURE;
michael@0 265 }
michael@0 266 }
michael@0 267
michael@0 268 nsAutoPtr<VideoData> video(mVideoQueue.PeekFront());
michael@0 269 if (video && video->GetEndTime() < aTime) {
michael@0 270 mVideoQueue.PopFront();
michael@0 271 video = nullptr;
michael@0 272 } else {
michael@0 273 video.forget();
michael@0 274 }
michael@0 275 }
michael@0 276
michael@0 277 return NS_OK;
michael@0 278 }
michael@0 279
michael@0 280 nsresult RawReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
michael@0 281 {
michael@0 282 return NS_OK;
michael@0 283 }

mercurial