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 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "MediaPluginReader.h"
7 #include "mozilla/TimeStamp.h"
8 #include "mozilla/dom/TimeRanges.h"
9 #include "MediaResource.h"
10 #include "VideoUtils.h"
11 #include "MediaPluginDecoder.h"
12 #include "MediaPluginHost.h"
13 #include "MediaDecoderStateMachine.h"
14 #include "ImageContainer.h"
15 #include "AbstractMediaDecoder.h"
19 typedef mozilla::layers::Image Image
;
21 MediaPluginReader::MediaPluginReader(AbstractMediaDecoder
*aDecoder
,
22 const nsACString
& aContentType
) :
23 MediaDecoderReader(aDecoder
),
34 MediaPluginReader::~MediaPluginReader()
39 nsresult
MediaPluginReader::Init(MediaDecoderReader
* aCloneDonor
)
44 nsresult
MediaPluginReader::ReadMetadata(VideoInfo
* aInfo
,
47 NS_ASSERTION(mDecoder
->OnDecodeThread(), "Should be on decode thread.");
50 mPlugin
= GetMediaPluginHost()->CreateDecoder(mDecoder
->GetResource(), mType
);
52 return NS_ERROR_FAILURE
;
56 // Set the total duration (the max of the audio and video track).
58 mPlugin
->GetDuration(mPlugin
, &durationUs
);
60 ReentrantMonitorAutoEnter
mon(mDecoder
->GetReentrantMonitor());
61 mDecoder
->SetMediaDuration(durationUs
);
64 if (mPlugin
->HasVideo(mPlugin
)) {
65 int32_t width
, height
;
66 mPlugin
->GetVideoParameters(mPlugin
, &width
, &height
);
67 nsIntRect
pictureRect(0, 0, width
, height
);
69 // Validate the container-reported frame and pictureRect sizes. This ensures
70 // that our video frame creation code doesn't overflow.
71 nsIntSize
displaySize(width
, height
);
72 nsIntSize
frameSize(width
, height
);
73 if (!VideoInfo::ValidateVideoRegion(frameSize
, pictureRect
, displaySize
)) {
74 return NS_ERROR_FAILURE
;
77 // Video track's frame sizes will not overflow. Activate the video track.
78 mHasVideo
= mInfo
.mHasVideo
= true;
79 mInfo
.mDisplay
= displaySize
;
80 mPicture
= pictureRect
;
81 mInitialFrame
= frameSize
;
82 VideoFrameContainer
* container
= mDecoder
->GetVideoFrameContainer();
84 container
->SetCurrentFrame(gfxIntSize(displaySize
.width
, displaySize
.height
),
86 mozilla::TimeStamp::Now());
90 if (mPlugin
->HasAudio(mPlugin
)) {
91 int32_t numChannels
, sampleRate
;
92 mPlugin
->GetAudioParameters(mPlugin
, &numChannels
, &sampleRate
);
93 mHasAudio
= mInfo
.mHasAudio
= true;
94 mInfo
.mAudioChannels
= numChannels
;
95 mInfo
.mAudioRate
= sampleRate
;
103 // Resets all state related to decoding, emptying all buffers etc.
104 nsresult
MediaPluginReader::ResetDecode()
106 if (mLastVideoFrame
) {
107 delete mLastVideoFrame
;
108 mLastVideoFrame
= NULL
;
111 GetMediaPluginHost()->DestroyDecoder(mPlugin
);
118 bool MediaPluginReader::DecodeVideoFrame(bool &aKeyframeSkip
,
119 int64_t aTimeThreshold
)
121 // Record number of frames decoded and parsed. Automatically update the
122 // stats counters using the AutoNotifyDecoded stack-based class.
123 uint32_t parsed
= 0, decoded
= 0;
124 AbstractMediaDecoder::AutoNotifyDecoded
autoNotify(mDecoder
, parsed
, decoded
);
126 // Throw away the currently buffered frame if we are seeking.
127 if (mLastVideoFrame
&& mVideoSeekTimeUs
!= -1) {
128 delete mLastVideoFrame
;
129 mLastVideoFrame
= NULL
;
132 ImageBufferCallback
bufferCallback(mDecoder
->GetImageContainer());
133 nsRefPtr
<Image
> currentImage
;
137 MPAPI::VideoFrame frame
;
138 if (!mPlugin
->ReadVideo(mPlugin
, &frame
, mVideoSeekTimeUs
, &bufferCallback
)) {
139 // We reached the end of the video stream. If we have a buffered
140 // video frame, push it the video queue using the total duration
141 // of the video as the end time.
142 if (mLastVideoFrame
) {
144 mPlugin
->GetDuration(mPlugin
, &durationUs
);
145 mLastVideoFrame
->mEndTime
= (durationUs
> mLastVideoFrame
->mTime
)
147 : mLastVideoFrame
->mTime
;
148 mVideoQueue
.Push(mLastVideoFrame
);
149 mLastVideoFrame
= NULL
;
151 mVideoQueue
.Finish();
154 mVideoSeekTimeUs
= -1;
157 // Disable keyframe skipping for now as
158 // stagefright doesn't seem to be telling us
159 // when a frame is a keyframe.
161 if (!frame
.mKeyFrame
) {
166 aKeyframeSkip
= false;
169 if (frame
.mSize
== 0)
172 currentImage
= bufferCallback
.GetImage();
173 int64_t pos
= mDecoder
->GetResource()->Tell();
174 nsIntRect picture
= mPicture
;
178 gfxIntSize frameSize
= currentImage
->GetSize();
179 if (frameSize
.width
!= mInitialFrame
.width
||
180 frameSize
.height
!= mInitialFrame
.height
) {
181 // Frame size is different from what the container reports. This is legal,
182 // and we will preserve the ratio of the crop rectangle as it
183 // was reported relative to the picture size reported by the container.
184 picture
.x
= (mPicture
.x
* frameSize
.width
) / mInitialFrame
.width
;
185 picture
.y
= (mPicture
.y
* frameSize
.height
) / mInitialFrame
.height
;
186 picture
.width
= (frameSize
.width
* mPicture
.width
) / mInitialFrame
.width
;
187 picture
.height
= (frameSize
.height
* mPicture
.height
) / mInitialFrame
.height
;
190 v
= VideoData::CreateFromImage(mInfo
,
191 mDecoder
->GetImageContainer(),
194 frame
.mTimeUs
+1, // We don't know the end time.
201 VideoData::YCbCrBuffer b
;
202 b
.mPlanes
[0].mData
= static_cast<uint8_t *>(frame
.Y
.mData
);
203 b
.mPlanes
[0].mStride
= frame
.Y
.mStride
;
204 b
.mPlanes
[0].mHeight
= frame
.Y
.mHeight
;
205 b
.mPlanes
[0].mWidth
= frame
.Y
.mWidth
;
206 b
.mPlanes
[0].mOffset
= frame
.Y
.mOffset
;
207 b
.mPlanes
[0].mSkip
= frame
.Y
.mSkip
;
209 b
.mPlanes
[1].mData
= static_cast<uint8_t *>(frame
.Cb
.mData
);
210 b
.mPlanes
[1].mStride
= frame
.Cb
.mStride
;
211 b
.mPlanes
[1].mHeight
= frame
.Cb
.mHeight
;
212 b
.mPlanes
[1].mWidth
= frame
.Cb
.mWidth
;
213 b
.mPlanes
[1].mOffset
= frame
.Cb
.mOffset
;
214 b
.mPlanes
[1].mSkip
= frame
.Cb
.mSkip
;
216 b
.mPlanes
[2].mData
= static_cast<uint8_t *>(frame
.Cr
.mData
);
217 b
.mPlanes
[2].mStride
= frame
.Cr
.mStride
;
218 b
.mPlanes
[2].mHeight
= frame
.Cr
.mHeight
;
219 b
.mPlanes
[2].mWidth
= frame
.Cr
.mWidth
;
220 b
.mPlanes
[2].mOffset
= frame
.Cr
.mOffset
;
221 b
.mPlanes
[2].mSkip
= frame
.Cr
.mSkip
;
223 if (frame
.Y
.mWidth
!= mInitialFrame
.width
||
224 frame
.Y
.mHeight
!= mInitialFrame
.height
) {
226 // Frame size is different from what the container reports. This is legal,
227 // and we will preserve the ratio of the crop rectangle as it
228 // was reported relative to the picture size reported by the container.
229 picture
.x
= (mPicture
.x
* frame
.Y
.mWidth
) / mInitialFrame
.width
;
230 picture
.y
= (mPicture
.y
* frame
.Y
.mHeight
) / mInitialFrame
.height
;
231 picture
.width
= (frame
.Y
.mWidth
* mPicture
.width
) / mInitialFrame
.width
;
232 picture
.height
= (frame
.Y
.mHeight
* mPicture
.height
) / mInitialFrame
.height
;
235 // This is the approximate byte position in the stream.
236 v
= VideoData::Create(mInfo
,
237 mDecoder
->GetImageContainer(),
240 frame
.mTimeUs
+1, // We don't know the end time.
252 NS_ASSERTION(decoded
<= parsed
, "Expect to decode fewer frames than parsed in MediaPlugin...");
254 // Since MPAPI doesn't give us the end time of frames, we keep one frame
255 // buffered in MediaPluginReader and push it into the queue as soon
256 // we read the following frame so we can use that frame's start time as
257 // the end time of the buffered frame.
258 if (!mLastVideoFrame
) {
263 mLastVideoFrame
->mEndTime
= v
->mTime
;
265 // We have the start time of the next frame, so we can push the previous
266 // frame into the queue, except if the end time is below the threshold,
267 // in which case it wouldn't be displayed anyway.
268 if (mLastVideoFrame
->mEndTime
< aTimeThreshold
) {
269 delete mLastVideoFrame
;
270 mLastVideoFrame
= NULL
;
274 mVideoQueue
.Push(mLastVideoFrame
);
276 // Buffer the current frame we just decoded.
285 bool MediaPluginReader::DecodeAudioData()
287 NS_ASSERTION(mDecoder
->OnDecodeThread(), "Should be on decode thread.");
289 // This is the approximate byte position in the stream.
290 int64_t pos
= mDecoder
->GetResource()->Tell();
293 MPAPI::AudioFrame frame
;
294 if (!mPlugin
->ReadAudio(mPlugin
, &frame
, mAudioSeekTimeUs
)) {
295 mAudioQueue
.Finish();
298 mAudioSeekTimeUs
= -1;
300 // Ignore empty buffers which stagefright media read will sporadically return
301 if (frame
.mSize
== 0)
304 nsAutoArrayPtr
<AudioDataValue
> buffer(new AudioDataValue
[frame
.mSize
/2] );
305 memcpy(buffer
.get(), frame
.mData
, frame
.mSize
);
307 uint32_t frames
= frame
.mSize
/ (2 * frame
.mAudioChannels
);
308 CheckedInt64 duration
= FramesToUsecs(frames
, frame
.mAudioSampleRate
);
309 if (!duration
.isValid()) {
313 mAudioQueue
.Push(new AudioData(pos
,
318 frame
.mAudioChannels
));
322 nsresult
MediaPluginReader::Seek(int64_t aTarget
, int64_t aStartTime
, int64_t aEndTime
, int64_t aCurrentTime
)
324 NS_ASSERTION(mDecoder
->OnDecodeThread(), "Should be on decode thread.");
329 mAudioSeekTimeUs
= mVideoSeekTimeUs
= aTarget
;
331 return DecodeToTarget(aTarget
);
334 nsresult
MediaPluginReader::GetBuffered(mozilla::dom::TimeRanges
* aBuffered
, int64_t aStartTime
)
339 MediaResource
* stream
= mDecoder
->GetResource();
341 int64_t durationUs
= 0;
342 mPlugin
->GetDuration(mPlugin
, &durationUs
);
344 GetEstimatedBufferedTimeRanges(stream
, durationUs
, aBuffered
);
349 MediaPluginReader::ImageBufferCallback::ImageBufferCallback(mozilla::layers::ImageContainer
*aImageContainer
) :
350 mImageContainer(aImageContainer
)
355 MediaPluginReader::ImageBufferCallback::operator()(size_t aWidth
, size_t aHeight
,
356 MPAPI::ColorFormat aColorFormat
)
358 if (!mImageContainer
) {
359 NS_WARNING("No image container to construct an image");
363 nsRefPtr
<Image
> rgbImage
;
364 switch(aColorFormat
) {
366 rgbImage
= mozilla::layers::CreateSharedRGBImage(mImageContainer
,
367 nsIntSize(aWidth
, aHeight
),
368 gfxASurface::ImageFormatRGB16_565
);
370 return rgbImage
->AsSharedImage()->GetBuffer();
373 NS_NOTREACHED("Color format not supported");
378 already_AddRefed
<Image
>
379 MediaPluginReader::ImageBufferCallback::GetImage()
381 return mImage
.forget();
384 } // namespace mozilla