Attachment #496253: Patch v2 1/3: base stats for bug #580531

View | Details | Raw Unified | Return to bug 580531
Collapse All | Expand All

(-)a/content/html/content/public/nsHTMLVideoElement.h (+5 lines)
Line     Link Here 
 Lines 78-88   public: Link Here 
78
78
79
  // Returns the current video frame width and height.
79
  // Returns the current video frame width and height.
80
  // If there is no video frame, returns the given default size.
80
  // If there is no video frame, returns the given default size.
81
  nsIntSize GetVideoSize(nsIntSize defaultSize);
81
  nsIntSize GetVideoSize(nsIntSize defaultSize);
82
82
83
  virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel);
83
  virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel);
84
84
85
  virtual nsXPCClassInfo* GetClassInfo();
85
  virtual nsXPCClassInfo* GetClassInfo();
86
87
  // Dispatches an event to increment the counter of the number of frames
88
  // painted. Called on the main thread by the nsVideoFrame when a new image
89
  // is painted.
90
  void NotifyPaintedFrame();
86
};
91
};
87
92
88
#endif
93
#endif
(-)a/content/html/content/src/nsHTMLVideoElement.cpp (+45 lines)
Line     Link Here 
 Lines 178-185   nsresult nsHTMLVideoElement::SetAcceptHe Link Here 
178
        "audio/*;q=0.6,*/*;q=0.5");
178
        "audio/*;q=0.6,*/*;q=0.5");
179
179
180
    return aChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
180
    return aChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
181
                                      value,
181
                                      value,
182
                                      PR_FALSE);
182
                                      PR_FALSE);
183
}
183
}
184
184
185
NS_IMPL_URI_ATTR(nsHTMLVideoElement, Poster, poster)
185
NS_IMPL_URI_ATTR(nsHTMLVideoElement, Poster, poster)
186
187
/* readonly attribute unsigned long mozDecodedFrames; */
188
NS_IMETHODIMP nsHTMLVideoElement::GetMozParsedFrames(PRUint32 *aMozDecodedFrames)
189
{
190
  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
191
  *aMozDecodedFrames = mDecoder ? mDecoder->GetParsedFrames() : 0;
192
  return NS_OK;
193
}
194
195
/* readonly attribute unsigned long mozDroppedFrames; */
196
NS_IMETHODIMP nsHTMLVideoElement::GetMozDecodedFrames(PRUint32 *aMozDroppedFrames)
197
{
198
  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
199
  *aMozDroppedFrames = mDecoder ? mDecoder->GetDecodedFrames() : 0;
200
  return NS_OK;
201
}
202
203
NS_IMETHODIMP nsHTMLVideoElement::GetMozPresentedFrames(PRUint32 *aMozDroppedFrames)
204
{
205
  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
206
  *aMozDroppedFrames = mDecoder ? mDecoder->GetPresentedFrames() : 0;
207
  return NS_OK;
208
}
209
210
NS_IMETHODIMP nsHTMLVideoElement::GetMozPaintedFrames(PRUint32 *aMozDroppedFrames)
211
{
212
  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
213
  *aMozDroppedFrames = mDecoder ? mDecoder->GetPaintedFrames() : 0;
214
  return NS_OK;
215
}
216
217
NS_IMETHODIMP nsHTMLVideoElement::GetMozFrameDelay(float *aMozFrameDelay)
218
{
219
  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
220
  *aMozFrameDelay = mImageContainer ? mImageContainer->GetPaintDelay().ToSeconds() : 0;
221
  return NS_OK;
222
}
223
224
void nsHTMLVideoElement::NotifyPaintedFrame()
225
{
226
  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
227
  if (mDecoder) {
228
    mDecoder->NotifyPaintedFrame();
229
  }
230
}
(-)a/content/media/nsBuiltinDecoderReader.cpp (-1 / +2 lines)
Line     Link Here 
 Lines 320-336   nsresult nsBuiltinDecoderReader::DecodeT Link Here 
320
{
320
{
321
  // Decode forward to the target frame. Start with video, if we have it.
321
  // Decode forward to the target frame. Start with video, if we have it.
322
  if (HasVideo()) {
322
  if (HasVideo()) {
323
    PRBool eof = PR_FALSE;
323
    PRBool eof = PR_FALSE;
324
    PRInt64 startTime = -1;
324
    PRInt64 startTime = -1;
325
    while (HasVideo() && !eof) {
325
    while (HasVideo() && !eof) {
326
      while (mVideoQueue.GetSize() == 0 && !eof) {
326
      while (mVideoQueue.GetSize() == 0 && !eof) {
327
        PRBool skip = PR_FALSE;
327
        PRBool skip = PR_FALSE;
328
        eof = !DecodeVideoFrame(skip, 0);
328
        PRUint32 parsed=0, decoded=0;
329
        eof = !DecodeVideoFrame(skip, 0, parsed, decoded);
329
        {
330
        {
330
          MonitorAutoExit exitReaderMon(mMonitor);
331
          MonitorAutoExit exitReaderMon(mMonitor);
331
          MonitorAutoEnter decoderMon(mDecoder->GetMonitor());
332
          MonitorAutoEnter decoderMon(mDecoder->GetMonitor());
332
          if (mDecoder->GetDecodeState() == nsBuiltinDecoderStateMachine::DECODER_STATE_SHUTDOWN) {
333
          if (mDecoder->GetDecodeState() == nsBuiltinDecoderStateMachine::DECODER_STATE_SHUTDOWN) {
333
            return NS_ERROR_FAILURE;
334
            return NS_ERROR_FAILURE;
334
          }
335
          }
335
        }
336
        }
336
      }
337
      }
(-)a/content/media/nsBuiltinDecoderReader.h (-3 / +8 lines)
Line     Link Here 
 Lines 444-461   public: Link Here 
444
  // in mAudioQueue. Returns PR_TRUE when there's more audio to decode,
444
  // in mAudioQueue. Returns PR_TRUE when there's more audio to decode,
445
  // PR_FALSE if the audio is finished, end of file has been reached,
445
  // PR_FALSE if the audio is finished, end of file has been reached,
446
  // or an un-recoverable read error has occured.
446
  // or an un-recoverable read error has occured.
447
  virtual PRBool DecodeAudioData() = 0;
447
  virtual PRBool DecodeAudioData() = 0;
448
448
449
  // Reads and decodes one video frame. Packets with a timestamp less
449
  // Reads and decodes one video frame. Packets with a timestamp less
450
  // than aTimeThreshold will be decoded (unless they're not keyframes
450
  // than aTimeThreshold will be decoded (unless they're not keyframes
451
  // and aKeyframeSkip is PR_TRUE), but will not be added to the queue.
451
  // and aKeyframeSkip is PR_TRUE), but will not be added to the queue.
452
  virtual PRBool DecodeVideoFrame(PRBool &aKeyframeSkip,
452
  virtual PRBool DecodeVideoFrame(PRBool& aKeyframeSkip,
453
                                  PRInt64 aTimeThreshold) = 0;
453
                                  PRInt64 aTimeThreshold,
454
                                  PRUint32& aParsed,
455
                                  PRUint32& aDecoded) = 0;
454
456
455
  virtual PRBool HasAudio() = 0;
457
  virtual PRBool HasAudio() = 0;
456
  virtual PRBool HasVideo() = 0;
458
  virtual PRBool HasVideo() = 0;
457
459
458
  // Read header data for all bitstreams in the file. Fills mInfo with
460
  // Read header data for all bitstreams in the file. Fills mInfo with
459
  // the data required to present the media. Returns NS_OK on success,
461
  // the data required to present the media. Returns NS_OK on success,
460
  // or NS_ERROR_FAILURE on failure.
462
  // or NS_ERROR_FAILURE on failure.
461
  virtual nsresult ReadMetadata() = 0;
463
  virtual nsresult ReadMetadata() = 0;
 Lines 515-531   protected: Link Here 
515
  template<class Data>
517
  template<class Data>
516
  Data* DecodeToFirstData(DecodeFn aDecodeFn,
518
  Data* DecodeToFirstData(DecodeFn aDecodeFn,
517
                          MediaQueue<Data>& aQueue);
519
                          MediaQueue<Data>& aQueue);
518
520
519
  // Wrapper so that DecodeVideoFrame(PRBool&,PRInt64) can be called from
521
  // Wrapper so that DecodeVideoFrame(PRBool&,PRInt64) can be called from
520
  // DecodeToFirstData().
522
  // DecodeToFirstData().
521
  PRBool DecodeVideoFrame() {
523
  PRBool DecodeVideoFrame() {
522
    PRBool f = PR_FALSE;
524
    PRBool f = PR_FALSE;
523
    return DecodeVideoFrame(f, 0);
525
    // Ignore these, DecodeVideoFrame() is only called during seeking, so
526
    // these aren't relevant to performance.
527
    PRUint32 parsed=0, decoded=0;
528
    return DecodeVideoFrame(f, 0, parsed, decoded);
524
  }
529
  }
525
530
526
  // Fills aRanges with ByteRanges denoting the sections of the media which
531
  // Fills aRanges with ByteRanges denoting the sections of the media which
527
  // have been downloaded and are stored in the media cache. The reader
532
  // have been downloaded and are stored in the media cache. The reader
528
  // monitor must must be held with exactly one lock count. The nsMediaStream
533
  // monitor must must be held with exactly one lock count. The nsMediaStream
529
  // must be pinned while calling this.
534
  // must be pinned while calling this.
530
  nsresult GetBufferedBytes(nsTArray<ByteRange>& aRanges);
535
  nsresult GetBufferedBytes(nsTArray<ByteRange>& aRanges);
531
536
(-)a/content/media/nsBuiltinDecoderStateMachine.cpp (-12 / +31 lines)
Line     Link Here 
 Lines 292-327   void nsBuiltinDecoderStateMachine::Decod Link Here 
292
        ((!audioPump && audioPlaying && audioDecoded < lowAudioThreshold) ||
292
        ((!audioPump && audioPlaying && audioDecoded < lowAudioThreshold) ||
293
         (!videoPump && videoQueueSize < LOW_VIDEO_FRAMES)))
293
         (!videoPump && videoQueueSize < LOW_VIDEO_FRAMES)))
294
    {
294
    {
295
      skipToNextKeyframe = PR_TRUE;
295
      skipToNextKeyframe = PR_TRUE;
296
      LOG(PR_LOG_DEBUG, ("Skipping video decode to the next keyframe"));
296
      LOG(PR_LOG_DEBUG, ("Skipping video decode to the next keyframe"));
297
    }
297
    }
298
298
299
    // Video decode.
299
    // Video decode.
300
    PRUint32 parsed = 0, decoded = 0;
300
    if (videoPlaying && !videoWait) {
301
    if (videoPlaying && !videoWait) {
301
      // Time the video decode, so that if it's slow, we can increase our low
302
      // Time the video decode, so that if it's slow, we can increase our low
302
      // audio threshold to reduce the chance of an audio underrun while we're
303
      // audio threshold to reduce the chance of an audio underrun while we're
303
      // waiting for a video decode to complete.
304
      // waiting for a video decode to complete.
304
      TimeStamp start = TimeStamp::Now();
305
      TimeStamp start = TimeStamp::Now();
305
      videoPlaying = mReader->DecodeVideoFrame(skipToNextKeyframe, currentTime);
306
      videoPlaying = mReader->DecodeVideoFrame(skipToNextKeyframe,
307
                                               currentTime,
308
                                               parsed,
309
                                               decoded);
306
      TimeDuration decodeTime = TimeStamp::Now() - start;
310
      TimeDuration decodeTime = TimeStamp::Now() - start;
307
      if (!decodeCloseToDownload &&
311
      if (!decodeCloseToDownload &&
308
          THRESHOLD_FACTOR * decodeTime.ToMilliseconds() > lowAudioThreshold)
312
          THRESHOLD_FACTOR * decodeTime.ToMilliseconds() > lowAudioThreshold)
309
      {
313
      {
310
        lowAudioThreshold =
314
        lowAudioThreshold =
311
          NS_MIN(static_cast<PRInt64>(THRESHOLD_FACTOR * decodeTime.ToMilliseconds()),
315
          NS_MIN(static_cast<PRInt64>(THRESHOLD_FACTOR * decodeTime.ToMilliseconds()),
312
                 static_cast<PRInt64>(AMPLE_AUDIO_MS));
316
                 static_cast<PRInt64>(AMPLE_AUDIO_MS));
313
        ampleAudioThreshold = NS_MAX(THRESHOLD_FACTOR * lowAudioThreshold,
317
        ampleAudioThreshold = NS_MAX(THRESHOLD_FACTOR * lowAudioThreshold,
314
                                     ampleAudioThreshold);
318
                                     ampleAudioThreshold);
315
        LOG(PR_LOG_DEBUG,
319
        LOG(PR_LOG_DEBUG,
316
            ("Slow video decode, set lowAudioThreshold=%lld ampleAudioThreshold=%lld",
320
            ("Slow video decode, set lowAudioThreshold=%lld ampleAudioThreshold=%lld",
317
             lowAudioThreshold, ampleAudioThreshold));
321
             lowAudioThreshold, ampleAudioThreshold));
318
      }
322
      }
319
    }
323
    }
324
    if (parsed || decoded) {
325
      mDecoder->NotifyDecodedFrames(parsed, decoded);
326
    }
320
    {
327
    {
321
      MonitorAutoEnter mon(mDecoder->GetMonitor());
328
      MonitorAutoEnter mon(mDecoder->GetMonitor());
322
      mDecoder->GetMonitor().NotifyAll();
329
      mDecoder->GetMonitor().NotifyAll();
323
    }
330
    }
324
331
325
    // Audio decode.
332
    // Audio decode.
326
    if (audioPlaying && !audioWait) {
333
    if (audioPlaying && !audioWait) {
327
      audioPlaying = mReader->DecodeAudioData();
334
      audioPlaying = mReader->DecodeAudioData();
 Lines 922-938   nsresult nsBuiltinDecoderStateMachine::R Link Here 
922
        LoadMetadata();
929
        LoadMetadata();
923
        if (mState == DECODER_STATE_SHUTDOWN) {
930
        if (mState == DECODER_STATE_SHUTDOWN) {
924
          continue;
931
          continue;
925
        }
932
        }
926
933
927
        VideoData* videoData = FindStartTime();
934
        VideoData* videoData = FindStartTime();
928
        if (videoData) {
935
        if (videoData) {
929
          MonitorAutoExit exitMon(mDecoder->GetMonitor());
936
          MonitorAutoExit exitMon(mDecoder->GetMonitor());
930
          RenderVideoFrame(videoData);
937
          RenderVideoFrame(videoData, TimeStamp::Now());
931
        }
938
        }
932
939
933
        // Start the decode threads, so that we can pre buffer the streams.
940
        // Start the decode threads, so that we can pre buffer the streams.
934
        // and calculate the start time in order to determine the duration.
941
        // and calculate the start time in order to determine the duration.
935
        if (NS_FAILED(StartDecodeThreads())) {
942
        if (NS_FAILED(StartDecodeThreads())) {
936
          continue;
943
          continue;
937
        }
944
        }
938
945
 Lines 1051-1073   nsresult nsBuiltinDecoderStateMachine::R Link Here 
1051
          }
1058
          }
1052
          if (NS_SUCCEEDED(res)){
1059
          if (NS_SUCCEEDED(res)){
1053
            SoundData* audio = HasAudio() ? mReader->mAudioQueue.PeekFront() : nsnull;
1060
            SoundData* audio = HasAudio() ? mReader->mAudioQueue.PeekFront() : nsnull;
1054
            NS_ASSERTION(!audio || (audio->mTime <= seekTime &&
1061
            NS_ASSERTION(!audio || (audio->mTime <= seekTime &&
1055
                                    seekTime <= audio->mTime + audio->mDuration),
1062
                                    seekTime <= audio->mTime + audio->mDuration),
1056
                         "Seek target should lie inside the first audio block after seek");
1063
                         "Seek target should lie inside the first audio block after seek");
1057
            PRInt64 startTime = (audio && audio->mTime < seekTime) ? audio->mTime : seekTime;
1064
            PRInt64 startTime = (audio && audio->mTime < seekTime) ? audio->mTime : seekTime;
1058
            mAudioStartTime = startTime;
1065
            mAudioStartTime = startTime;
1059
            mPlayDuration = TimeDuration::FromMilliseconds(startTime);
1066
            mPlayDuration = TimeDuration::FromMilliseconds(startTime - mStartTime);
1060
            if (HasVideo()) {
1067
            if (HasVideo()) {
1061
              nsAutoPtr<VideoData> video(mReader->mVideoQueue.PeekFront());
1068
              nsAutoPtr<VideoData> video(mReader->mVideoQueue.PeekFront());
1062
              if (video) {
1069
              if (video) {
1063
                NS_ASSERTION(video->mTime <= seekTime && seekTime <= video->mEndTime,
1070
                NS_ASSERTION(video->mTime <= seekTime && seekTime <= video->mEndTime,
1064
                             "Seek target should lie inside the first frame after seek");
1071
                             "Seek target should lie inside the first frame after seek");
1065
                RenderVideoFrame(video);
1072
                RenderVideoFrame(video, TimeStamp::Now());
1066
                mReader->mVideoQueue.PopFront();
1073
                mReader->mVideoQueue.PopFront();
1067
              }
1074
              }
1068
            }
1075
            }
1069
          }
1076
          }
1070
        }
1077
        }
1071
        mDecoder->StartProgressUpdates();
1078
        mDecoder->StartProgressUpdates();
1072
        if (mState == DECODER_STATE_SHUTDOWN)
1079
        if (mState == DECODER_STATE_SHUTDOWN)
1073
          continue;
1080
          continue;
 Lines 1193-1220   nsresult nsBuiltinDecoderStateMachine::R Link Here 
1193
      }
1200
      }
1194
      break;
1201
      break;
1195
    }
1202
    }
1196
  }
1203
  }
1197
1204
1198
  return NS_OK;
1205
  return NS_OK;
1199
}
1206
}
1200
1207
1201
void nsBuiltinDecoderStateMachine::RenderVideoFrame(VideoData* aData)
1208
void nsBuiltinDecoderStateMachine::RenderVideoFrame(VideoData* aData, TimeStamp aTarget)
1202
{
1209
{
1203
  NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
1210
  NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
1204
1211
1205
  if (aData->mDuplicate) {
1212
  if (aData->mDuplicate) {
1206
    return;
1213
    return;
1207
  }
1214
  }
1208
1215
1209
  nsRefPtr<Image> image = aData->mImage;
1216
  nsRefPtr<Image> image = aData->mImage;
1210
  if (image) {
1217
  if (image) {
1211
    const nsVideoInfo& info = mReader->GetInfo();
1218
    const nsVideoInfo& info = mReader->GetInfo();
1212
    mDecoder->SetVideoData(gfxIntSize(info.mPicture.width, info.mPicture.height), info.mPixelAspectRatio, image);
1219
    mDecoder->SetVideoData(gfxIntSize(info.mPicture.width, info.mPicture.height),
1220
                           info.mPixelAspectRatio,
1221
                           image,
1222
                           aTarget);
1213
  }
1223
  }
1214
}
1224
}
1215
1225
1216
PRInt64
1226
PRInt64
1217
nsBuiltinDecoderStateMachine::GetAudioClock()
1227
nsBuiltinDecoderStateMachine::GetAudioClock()
1218
{
1228
{
1219
  NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
1229
  NS_ASSERTION(IsCurrentThread(mDecoder->mStateMachineThread), "Should be on state machine thread.");
1220
  if (!mAudioStream || !HasAudio())
1230
  if (!mAudioStream || !HasAudio())
 Lines 1249-1274   void nsBuiltinDecoderStateMachine::Advan Link Here 
1249
    // the end of the audio, use the audio clock. However if we've finished
1259
    // the end of the audio, use the audio clock. However if we've finished
1250
    // audio, or don't have audio, use the system clock.
1260
    // audio, or don't have audio, use the system clock.
1251
    PRInt64 clock_time = -1;
1261
    PRInt64 clock_time = -1;
1252
    PRInt64 audio_time = GetAudioClock();
1262
    PRInt64 audio_time = GetAudioClock();
1253
    if (HasAudio() && !mAudioCompleted && audio_time != -1) {
1263
    if (HasAudio() && !mAudioCompleted && audio_time != -1) {
1254
      clock_time = audio_time;
1264
      clock_time = audio_time;
1255
      // Resync against the audio clock, while we're trusting the
1265
      // Resync against the audio clock, while we're trusting the
1256
      // audio clock. This ensures no "drift", particularly on Linux.
1266
      // audio clock. This ensures no "drift", particularly on Linux.
1257
      mPlayStartTime = TimeStamp::Now() - TimeDuration::FromMilliseconds(clock_time);
1267
      mPlayDuration = TimeDuration::FromMilliseconds(clock_time);
1268
      mPlayStartTime = TimeStamp::Now();
1258
    } else {
1269
    } else {
1259
      // Sound is disabled on this system. Sync to the system clock.
1270
      // Sound is disabled on this system. Sync to the system clock.
1260
      TimeDuration t = TimeStamp::Now() - mPlayStartTime + mPlayDuration;
1271
      TimeDuration t = TimeStamp::Now() - mPlayStartTime + mPlayDuration;
1261
      clock_time = (PRInt64)(1000 * t.ToSeconds());
1272
      clock_time = (PRInt64)(1000 * t.ToSeconds());
1262
      // Ensure the clock can never go backwards.
1273
      // Ensure the clock can never go backwards.
1263
      NS_ASSERTION(mCurrentFrameTime <= clock_time, "Clock should go forwards");
1274
      NS_ASSERTION(mCurrentFrameTime <= clock_time, "Clock should go forwards");
1264
      clock_time = NS_MAX(mCurrentFrameTime, clock_time) + mStartTime;
1275
      clock_time = NS_MAX(mCurrentFrameTime, clock_time) + mStartTime;
1265
    }
1276
    }
1266
1277
1278
    // Skip video frames up to the current playback position.
1267
    NS_ASSERTION(clock_time >= mStartTime, "Should have positive clock time.");
1279
    NS_ASSERTION(clock_time >= mStartTime, "Should have positive clock time.");
1268
    nsAutoPtr<VideoData> videoData;
1280
    nsAutoPtr<VideoData> videoData;
1269
    if (mReader->mVideoQueue.GetSize() > 0) {
1281
    if (mReader->mVideoQueue.GetSize() > 0) {
1270
      VideoData* data = mReader->mVideoQueue.PeekFront();
1282
      VideoData* data = mReader->mVideoQueue.PeekFront();
1271
      while (clock_time >= data->mTime) {
1283
      while (clock_time >= data->mTime) {
1272
        mVideoFrameEndTime = data->mEndTime;
1284
        mVideoFrameEndTime = data->mEndTime;
1273
        videoData = data;
1285
        videoData = data;
1274
        mReader->mVideoQueue.PopFront();
1286
        mReader->mVideoQueue.PopFront();
 Lines 1276-1299   void nsBuiltinDecoderStateMachine::Advan Link Here 
1276
        if (mReader->mVideoQueue.GetSize() == 0)
1288
        if (mReader->mVideoQueue.GetSize() == 0)
1277
          break;
1289
          break;
1278
        data = mReader->mVideoQueue.PeekFront();
1290
        data = mReader->mVideoQueue.PeekFront();
1279
      }
1291
      }
1280
    }
1292
    }
1281
1293
1282
    PRInt64 frameDuration = AUDIO_DURATION_MS;
1294
    PRInt64 frameDuration = AUDIO_DURATION_MS;
1283
    if (videoData) {
1295
    if (videoData) {
1284
      // Decode one frame and display it
1296
      // Display the next frame.
1285
      NS_ASSERTION(videoData->mTime >= mStartTime, "Should have positive frame time");
1297
      NS_ASSERTION(videoData->mTime >= mStartTime,
1298
                   "Should have positive frame time");
1299
1300
      // Calculate the system-clock time at which the frame should start being displayed.
1301
      TimeStamp presTime = mPlayStartTime - mPlayDuration +
1302
        TimeDuration::FromMilliseconds(videoData->mTime - mStartTime);
1303
1304
      NS_ASSERTION(presTime <= TimeStamp::Now(),
1305
                   "Frame must start before or at now");
1286
      {
1306
      {
1287
        MonitorAutoExit exitMon(mDecoder->GetMonitor());
1307
        MonitorAutoExit exitMon(mDecoder->GetMonitor());
1288
        // If we have video, we want to increment the clock in steps of the frame
1308
        RenderVideoFrame(videoData, presTime);
1289
        // duration.
1290
        RenderVideoFrame(videoData);
1291
      }
1309
      }
1310
      mDecoder->NotifyPresentedFrame();
1292
      frameDuration = videoData->mEndTime - videoData->mTime;
1311
      frameDuration = videoData->mEndTime - videoData->mTime;
1293
      videoData = nsnull;
1312
      videoData = nsnull;
1294
    }
1313
    }
1295
1314
1296
    // Kick the decode thread in case it filled its buffers and put itself
1315
    // Kick the decode thread in case it filled its buffers and put itself
1297
    // to sleep.
1316
    // to sleep.
1298
    mDecoder->GetMonitor().NotifyAll();
1317
    mDecoder->GetMonitor().NotifyAll();
1299
1318
(-)a/content/media/nsBuiltinDecoderStateMachine.h (-1 / +1 lines)
Line     Link Here 
 Lines 295-311   protected: Link Here 
295
295
296
  // Finds the end time of the last frame of data in the file, storing the value
296
  // Finds the end time of the last frame of data in the file, storing the value
297
  // in mEndTime if successful. The decoder must be held with exactly one lock
297
  // in mEndTime if successful. The decoder must be held with exactly one lock
298
  // count. Called on the state machine thread.
298
  // count. Called on the state machine thread.
299
  void FindEndTime();
299
  void FindEndTime();
300
300
301
  // Performs YCbCr to RGB conversion, and pushes the image down the
301
  // Performs YCbCr to RGB conversion, and pushes the image down the
302
  // rendering pipeline. Called on the state machine thread.
302
  // rendering pipeline. Called on the state machine thread.
303
  void RenderVideoFrame(VideoData* aData);
303
  void RenderVideoFrame(VideoData* aData, TimeStamp aTarget);
304
304
305
  // If we have video, display a video frame if it's time for display has
305
  // If we have video, display a video frame if it's time for display has
306
  // arrived, otherwise sleep until it's time for the next sample. Update
306
  // arrived, otherwise sleep until it's time for the next sample. Update
307
  // the current frame time as appropriate, and trigger ready state update.
307
  // the current frame time as appropriate, and trigger ready state update.
308
  // The decoder monitor must be held with exactly one lock count. Called
308
  // The decoder monitor must be held with exactly one lock count. Called
309
  // on the state machine thread.
309
  // on the state machine thread.
310
  void AdvanceFrame();
310
  void AdvanceFrame();
311
311
(-)a/content/media/nsMediaDecoder.cpp (-1 / +36 lines)
Line     Link Here 
 Lines 70-85    Link Here 
70
// nsMediaDecoder::CanPlayThrough() calculation more stable in the case of
70
// nsMediaDecoder::CanPlayThrough() calculation more stable in the case of
71
// fluctuating bitrates.
71
// fluctuating bitrates.
72
#define CAN_PLAY_THROUGH_MARGIN 20
72
#define CAN_PLAY_THROUGH_MARGIN 20
73
73
74
nsMediaDecoder::nsMediaDecoder() :
74
nsMediaDecoder::nsMediaDecoder() :
75
  mElement(0),
75
  mElement(0),
76
  mRGBWidth(-1),
76
  mRGBWidth(-1),
77
  mRGBHeight(-1),
77
  mRGBHeight(-1),
78
  mStatsMonitor("nsMediaDecoder.stats"),
79
  mParsedFrames(0),
80
  mDecodedFrames(0),
81
  mPresentedFrames(0),
82
  mPaintedFrames(0),
78
  mVideoUpdateLock(nsnull),
83
  mVideoUpdateLock(nsnull),
79
  mPixelAspectRatio(1.0),
84
  mPixelAspectRatio(1.0),
80
  mFrameBufferLength(0),
85
  mFrameBufferLength(0),
81
  mPinnedForSeek(PR_FALSE),
86
  mPinnedForSeek(PR_FALSE),
82
  mSizeChanged(PR_FALSE),
87
  mSizeChanged(PR_FALSE),
83
  mShuttingDown(PR_FALSE)
88
  mShuttingDown(PR_FALSE)
84
{
89
{
85
  MOZ_COUNT_CTOR(nsMediaDecoder);
90
  MOZ_COUNT_CTOR(nsMediaDecoder);
 Lines 240-268   void nsMediaDecoder::FireTimeUpdate() Link Here 
240
{
245
{
241
  if (!mElement)
246
  if (!mElement)
242
    return;
247
    return;
243
  mElement->FireTimeUpdate(PR_TRUE);
248
  mElement->FireTimeUpdate(PR_TRUE);
244
}
249
}
245
250
246
void nsMediaDecoder::SetVideoData(const gfxIntSize& aSize,
251
void nsMediaDecoder::SetVideoData(const gfxIntSize& aSize,
247
                                  float aPixelAspectRatio,
252
                                  float aPixelAspectRatio,
248
                                  Image* aImage)
253
                                  Image* aImage,
254
                                  TimeStamp aTarget)
249
{
255
{
250
  nsAutoLock lock(mVideoUpdateLock);
256
  nsAutoLock lock(mVideoUpdateLock);
251
257
252
  if (mRGBWidth != aSize.width || mRGBHeight != aSize.height ||
258
  if (mRGBWidth != aSize.width || mRGBHeight != aSize.height ||
253
      mPixelAspectRatio != aPixelAspectRatio) {
259
      mPixelAspectRatio != aPixelAspectRatio) {
254
    mRGBWidth = aSize.width;
260
    mRGBWidth = aSize.width;
255
    mRGBHeight = aSize.height;
261
    mRGBHeight = aSize.height;
256
    mPixelAspectRatio = aPixelAspectRatio;
262
    mPixelAspectRatio = aPixelAspectRatio;
257
    mSizeChanged = PR_TRUE;
263
    mSizeChanged = PR_TRUE;
258
  }
264
  }
259
  if (mImageContainer && aImage) {
265
  if (mImageContainer && aImage) {
260
    mImageContainer->SetCurrentImage(aImage);
266
    mImageContainer->SetCurrentImage(aImage);
267
    mImageContainer->SetPaintTarget(aTarget);
261
  }
268
  }
262
}
269
}
263
270
264
void nsMediaDecoder::PinForSeek()
271
void nsMediaDecoder::PinForSeek()
265
{
272
{
266
  nsMediaStream* stream = GetCurrentStream();
273
  nsMediaStream* stream = GetCurrentStream();
267
  if (!stream || mPinnedForSeek) {
274
  if (!stream || mPinnedForSeek) {
268
    return;
275
    return;
 Lines 310-317   PRBool nsMediaDecoder::CanPlayThrough() Link Here 
310
  // playback position, so that if the bitrate of the media fluctuates, or if
317
  // playback position, so that if the bitrate of the media fluctuates, or if
311
  // our download rate or decode rate estimation is otherwise inaccurate,
318
  // our download rate or decode rate estimation is otherwise inaccurate,
312
  // we don't suddenly discover that we need to buffer. This is particularly
319
  // we don't suddenly discover that we need to buffer. This is particularly
313
  // required near the start of the media, when not much data is downloaded.
320
  // required near the start of the media, when not much data is downloaded.
314
  PRInt64 readAheadMargin = stats.mPlaybackRate * CAN_PLAY_THROUGH_MARGIN;
321
  PRInt64 readAheadMargin = stats.mPlaybackRate * CAN_PLAY_THROUGH_MARGIN;
315
  return stats.mTotalBytes == stats.mDownloadPosition ||
322
  return stats.mTotalBytes == stats.mDownloadPosition ||
316
         stats.mDownloadPosition > stats.mPlaybackPosition + readAheadMargin;
323
         stats.mDownloadPosition > stats.mPlaybackPosition + readAheadMargin;
317
}
324
}
325
326
void nsMediaDecoder::NotifyPaintedFrame() {
327
  mozilla::MonitorAutoEnter mon(mStatsMonitor);
328
  ++mPaintedFrames;
329
}
330
331
void nsMediaDecoder::NotifyPresentedFrame() {
332
  mozilla::MonitorAutoEnter mon(mStatsMonitor);
333
  ++mPresentedFrames;
334
}
335
336
void nsMediaDecoder::NotifyDecodedFrames(PRUint32 aParsed, PRUint32 aDecoded) {
337
  mozilla::MonitorAutoEnter mon(mStatsMonitor);
338
  mParsedFrames += aParsed;
339
  mDecodedFrames += aDecoded;
340
}
341
342
#define IMPL_GET_STAT_METHOD(X) \
343
PRUint32 nsMediaDecoder::Get##X##Frames() { \
344
  mozilla::MonitorAutoEnter mon(mStatsMonitor); \
345
  return m##X##Frames; \
346
}
347
348
IMPL_GET_STAT_METHOD(Parsed);
349
IMPL_GET_STAT_METHOD(Decoded);
350
IMPL_GET_STAT_METHOD(Presented);
351
IMPL_GET_STAT_METHOD(Painted);
352
(-)a/content/media/nsMediaDecoder.h (-1 / +36 lines)
Line     Link Here 
 Lines 42-57    Link Here 
42
42
43
#include "nsIPrincipal.h"
43
#include "nsIPrincipal.h"
44
#include "nsSize.h"
44
#include "nsSize.h"
45
#include "prlog.h"
45
#include "prlog.h"
46
#include "gfxContext.h"
46
#include "gfxContext.h"
47
#include "gfxRect.h"
47
#include "gfxRect.h"
48
#include "nsITimer.h"
48
#include "nsITimer.h"
49
#include "ImageLayers.h"
49
#include "ImageLayers.h"
50
#include "mozilla/Monitor.h"
50
51
51
class nsHTMLMediaElement;
52
class nsHTMLMediaElement;
52
class nsMediaStream;
53
class nsMediaStream;
53
class nsIStreamListener;
54
class nsIStreamListener;
54
class nsTimeRanges;
55
class nsTimeRanges;
55
56
56
// The size to use for audio data frames in MozAudioAvailable events.
57
// The size to use for audio data frames in MozAudioAvailable events.
57
// This value is per channel, and is chosen to give ~43 fps of events,
58
// This value is per channel, and is chosen to give ~43 fps of events,
 Lines 82-97   private: Link Here 
82
// which can be called from any thread.
83
// which can be called from any thread.
83
class nsMediaDecoder : public nsIObserver
84
class nsMediaDecoder : public nsIObserver
84
{
85
{
85
public:
86
public:
86
  typedef mozilla::TimeStamp TimeStamp;
87
  typedef mozilla::TimeStamp TimeStamp;
87
  typedef mozilla::TimeDuration TimeDuration;
88
  typedef mozilla::TimeDuration TimeDuration;
88
  typedef mozilla::layers::ImageContainer ImageContainer;
89
  typedef mozilla::layers::ImageContainer ImageContainer;
89
  typedef mozilla::layers::Image Image;
90
  typedef mozilla::layers::Image Image;
91
  typedef mozilla::Monitor Monitor;
90
92
91
  nsMediaDecoder();
93
  nsMediaDecoder();
92
  virtual ~nsMediaDecoder();
94
  virtual ~nsMediaDecoder();
93
95
94
  // Create a new decoder of the same type as this one.
96
  // Create a new decoder of the same type as this one.
95
  virtual nsMediaDecoder* Clone() = 0;
97
  virtual nsMediaDecoder* Clone() = 0;
96
98
97
  // Perform any initialization required for the decoder.
99
  // Perform any initialization required for the decoder.
 Lines 271-296   public: Link Here 
271
  // the element is not a video element. This can be called from any
273
  // the element is not a video element. This can be called from any
272
  // thread; ImageContainers can be used from any thread.
274
  // thread; ImageContainers can be used from any thread.
273
  ImageContainer* GetImageContainer() { return mImageContainer; }
275
  ImageContainer* GetImageContainer() { return mImageContainer; }
274
276
275
  // Set the video width, height, pixel aspect ratio, and current image.
277
  // Set the video width, height, pixel aspect ratio, and current image.
276
  // Ownership of the image is transferred to the decoder.
278
  // Ownership of the image is transferred to the decoder.
277
  void SetVideoData(const gfxIntSize& aSize,
279
  void SetVideoData(const gfxIntSize& aSize,
278
                    float aPixelAspectRatio,
280
                    float aPixelAspectRatio,
279
                    Image* aImage);
281
                    Image* aImage,
282
                    TimeStamp aTarget);
280
283
281
  // Constructs the time ranges representing what segments of the media
284
  // Constructs the time ranges representing what segments of the media
282
  // are buffered and playable.
285
  // are buffered and playable.
283
  virtual nsresult GetBuffered(nsTimeRanges* aBuffered) = 0;
286
  virtual nsresult GetBuffered(nsTimeRanges* aBuffered) = 0;
284
287
285
  // Returns PR_TRUE if we can play the entire media through without stopping
288
  // Returns PR_TRUE if we can play the entire media through without stopping
286
  // to buffer, given the current download and playback rates.
289
  // to buffer, given the current download and playback rates.
287
  PRBool CanPlayThrough();
290
  PRBool CanPlayThrough();
288
291
292
  // Returns number of frames which have been parsed from the media.
293
  // Can be called on any thread.
294
  PRUint32 GetParsedFrames();
295
296
  // Returns the number of parsed frames which have been decoded.
297
  // Can be called on any thread.
298
  PRUint32 GetDecodedFrames();
299
300
  // Returns the number of decoded frames which have been sent to the rendering
301
  // pipeline for painting ("presented").
302
  // Can be called on any thread.
303
  PRUint32 GetPresentedFrames();
304
305
  // Returns the number of presented frames which ended up being painted.
306
  // Can be called on any thread.
307
  PRUint32 GetPaintedFrames();
308
309
  // Playback statistics gathering functions. Called when frames reach various
310
  // stages through the decode/rendering pipeline. Can be called on any thread.
311
  void NotifyDecodedFrames(PRUint32 aParsed, PRUint32 aDecoded);
312
  void NotifyPresentedFrame();
313
  void NotifyPaintedFrame();
314
289
protected:
315
protected:
290
316
291
  // Start timer to update download progress information.
317
  // Start timer to update download progress information.
292
  nsresult StartProgress();
318
  nsresult StartProgress();
293
319
294
  // Stop progress information timer.
320
  // Stop progress information timer.
295
  nsresult StopProgress();
321
  nsresult StopProgress();
296
322
 Lines 307-322   protected: Link Here 
307
  // This should only ever be accessed from the main thread.
333
  // This should only ever be accessed from the main thread.
308
  // It is set in Init and cleared in Shutdown when the element goes away.
334
  // It is set in Init and cleared in Shutdown when the element goes away.
309
  // The decoder does not add a reference the element.
335
  // The decoder does not add a reference the element.
310
  nsHTMLMediaElement* mElement;
336
  nsHTMLMediaElement* mElement;
311
337
312
  PRInt32 mRGBWidth;
338
  PRInt32 mRGBWidth;
313
  PRInt32 mRGBHeight;
339
  PRInt32 mRGBHeight;
314
340
341
  // Monitor to protect access of playback statistics.
342
  Monitor mStatsMonitor;
343
344
  // Playback statistics counters. Access protected by mStatsMonitor;
345
  PRUint32 mParsedFrames;
346
  PRUint32 mDecodedFrames;
347
  PRUint32 mPresentedFrames;
348
  PRUint32 mPaintedFrames;
349
  
315
  nsRefPtr<ImageContainer> mImageContainer;
350
  nsRefPtr<ImageContainer> mImageContainer;
316
351
317
  // Time that the last progress event was fired. Read/Write from the
352
  // Time that the last progress event was fired. Read/Write from the
318
  // main thread only.
353
  // main thread only.
319
  TimeStamp mProgressTime;
354
  TimeStamp mProgressTime;
320
355
321
  // Time that data was last read from the media resource. Used for
356
  // Time that data was last read from the media resource. Used for
322
  // computing if the download has stalled and to rate limit progress events
357
  // computing if the download has stalled and to rate limit progress events
(-)a/content/media/ogg/nsOggReader.cpp (-4 / +11 lines)
Line     Link Here 
 Lines 282-298   nsresult nsOggReader::ReadMetadata() Link Here 
282
282
283
  // Initialize the first Theora and Vorbis bitstreams. According to the
283
  // Initialize the first Theora and Vorbis bitstreams. According to the
284
  // Theora spec these can be considered the 'primary' bitstreams for playback.
284
  // Theora spec these can be considered the 'primary' bitstreams for playback.
285
  // Extract the metadata needed from these streams.
285
  // Extract the metadata needed from these streams.
286
  // Set a default callback period for if we have no video data
286
  // Set a default callback period for if we have no video data
287
  if (mTheoraState && mTheoraState->Init()) {
287
  if (mTheoraState && mTheoraState->Init()) {
288
    gfxIntSize sz(mTheoraState->mInfo.pic_width,
288
    gfxIntSize sz(mTheoraState->mInfo.pic_width,
289
                  mTheoraState->mInfo.pic_height);
289
                  mTheoraState->mInfo.pic_height);
290
    mDecoder->SetVideoData(sz, mTheoraState->mPixelAspectRatio, nsnull);
290
    mDecoder->SetVideoData(sz, mTheoraState->mPixelAspectRatio, nsnull, TimeStamp::Now());
291
  }
291
  }
292
  if (mVorbisState) {
292
  if (mVorbisState) {
293
    mVorbisState->Init();
293
    mVorbisState->Init();
294
  }
294
  }
295
295
296
  if (!HasAudio() && !HasVideo() && mSkeletonState) {
296
  if (!HasAudio() && !HasVideo() && mSkeletonState) {
297
    // We have a skeleton track, but no audio or video, may as well disable
297
    // We have a skeleton track, but no audio or video, may as well disable
298
    // the skeleton, we can't do anything useful with this media.
298
    // the skeleton, we can't do anything useful with this media.
 Lines 559-577   nsresult nsOggReader::DecodeTheora(nsTAr Link Here 
559
    }
559
    }
560
    if (!aFrames.AppendElement(v)) {
560
    if (!aFrames.AppendElement(v)) {
561
      delete v;
561
      delete v;
562
    }
562
    }
563
  }
563
  }
564
  return NS_OK;
564
  return NS_OK;
565
}
565
}
566
566
567
PRBool nsOggReader::DecodeVideoFrame(PRBool &aKeyframeSkip,
567
PRBool nsOggReader::DecodeVideoFrame(PRBool& aKeyframeSkip,
568
                                     PRInt64 aTimeThreshold)
568
                                     PRInt64 aTimeThreshold,
569
                                     PRUint32& aParsed,
570
                                     PRUint32& aDecoded)
569
{
571
{
572
  aParsed = aDecoded = 0;
570
  MonitorAutoEnter mon(mMonitor);
573
  MonitorAutoEnter mon(mMonitor);
571
  NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
574
  NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
572
               "Should be on state machine or AV thread.");
575
               "Should be on state machine or AV thread.");
573
  // We chose to keep track of the Theora granulepos ourselves, rather than
576
  // We chose to keep track of the Theora granulepos ourselves, rather than
574
  // rely on th_decode_packetin() to do it for us. This is because
577
  // rely on th_decode_packetin() to do it for us. This is because
575
  // th_decode_packetin() simply works by incrementing a counter every time
578
  // th_decode_packetin() simply works by incrementing a counter every time
576
  // it's called, so if we drop frames and don't call it, subsequent granulepos
579
  // it's called, so if we drop frames and don't call it, subsequent granulepos
577
  // will be wrong. Whenever we read a packet which has a granulepos, we use
580
  // will be wrong. Whenever we read a packet which has a granulepos, we use
 Lines 591-606   PRBool nsOggReader::DecodeVideoFrame(PRB Link Here 
591
        // Failed to read another page, must be the end of file. We can't have
594
        // Failed to read another page, must be the end of file. We can't have
592
        // already encountered an end of bitstream packet, else we wouldn't be
595
        // already encountered an end of bitstream packet, else we wouldn't be
593
        // here, so this bitstream must be missing its end of stream packet, or
596
        // here, so this bitstream must be missing its end of stream packet, or
594
        // is otherwise corrupt (oggz-chop can output files like this). Inform
597
        // is otherwise corrupt (oggz-chop can output files like this). Inform
595
        // the queue that there will be no more frames.
598
        // the queue that there will be no more frames.
596
        mVideoQueue.Finish();
599
        mVideoQueue.Finish();
597
        return PR_FALSE;
600
        return PR_FALSE;
598
      }
601
      }
602
      aParsed++;
599
603
600
      if (packet.granulepos > 0) {
604
      if (packet.granulepos > 0) {
601
        // We've found a packet with a granulepos, we can now determine the
605
        // We've found a packet with a granulepos, we can now determine the
602
        // buffered packet's timestamps, as well as the timestamps for any
606
        // buffered packet's timestamps, as well as the timestamps for any
603
        // packets we read subsequently.
607
        // packets we read subsequently.
604
        mTheoraGranulepos = packet.granulepos;
608
        mTheoraGranulepos = packet.granulepos;
605
      }
609
      }
606
    
610
    
 Lines 677-692   PRBool nsOggReader::DecodeVideoFrame(PRB Link Here 
677
    NS_ASSERTION(mTheoraGranulepos > 0, "We must Theora granulepos!");
681
    NS_ASSERTION(mTheoraGranulepos > 0, "We must Theora granulepos!");
678
    
682
    
679
    if (!ReadOggPacket(mTheoraState, &packet)) {
683
    if (!ReadOggPacket(mTheoraState, &packet)) {
680
      // Failed to read from file, so EOF or other premature failure.
684
      // Failed to read from file, so EOF or other premature failure.
681
      // Inform the queue that there will be no more frames.
685
      // Inform the queue that there will be no more frames.
682
      mVideoQueue.Finish();
686
      mVideoQueue.Finish();
683
      return PR_FALSE;
687
      return PR_FALSE;
684
    }
688
    }
689
    aParsed++;
685
690
686
    endOfStream = packet.e_o_s != 0;
691
    endOfStream = packet.e_o_s != 0;
687
692
688
    // Maintain the Theora granulepos. We must do this even if we drop frames,
693
    // Maintain the Theora granulepos. We must do this even if we drop frames,
689
    // otherwise our clock will be wrong after we've skipped frames.
694
    // otherwise our clock will be wrong after we've skipped frames.
690
    if (packet.granulepos != -1) {
695
    if (packet.granulepos != -1) {
691
      // Incoming packet has a granulepos, use that as it's granulepos.
696
      // Incoming packet has a granulepos, use that as it's granulepos.
692
      mTheoraGranulepos = packet.granulepos;
697
      mTheoraGranulepos = packet.granulepos;
 Lines 729-744   PRBool nsOggReader::DecodeVideoFrame(PRB Link Here 
729
  for (PRUint32 i = 0; i < frames.Length(); i++) {
734
  for (PRUint32 i = 0; i < frames.Length(); i++) {
730
    nsAutoPtr<VideoData> data(frames[i].forget());
735
    nsAutoPtr<VideoData> data(frames[i].forget());
731
    if (aKeyframeSkip && data->mKeyframe) {
736
    if (aKeyframeSkip && data->mKeyframe) {
732
      aKeyframeSkip = PR_FALSE;
737
      aKeyframeSkip = PR_FALSE;
733
    }
738
    }
734
739
735
    if (!aKeyframeSkip) {
740
    if (!aKeyframeSkip) {
736
      mVideoQueue.Push(data.forget());
741
      mVideoQueue.Push(data.forget());
742
      aDecoded++;
737
    }
743
    }
738
  }
744
  }
739
745
740
  if (endOfStream) {
746
  if (endOfStream) {
741
    // We've encountered an end of bitstream packet. Inform the queue that
747
    // We've encountered an end of bitstream packet. Inform the queue that
742
    // there will be no more frames.
748
    // there will be no more frames.
743
    mVideoQueue.Finish();
749
    mVideoQueue.Finish();
744
  }
750
  }
 Lines 1102-1118   nsresult nsOggReader::SeekInBufferedRang Link Here 
1102
    return res;
1108
    return res;
1103
  }
1109
  }
1104
1110
1105
  // We have an active Theora bitstream. Decode the next Theora frame, and
1111
  // We have an active Theora bitstream. Decode the next Theora frame, and
1106
  // extract its keyframe's time.
1112
  // extract its keyframe's time.
1107
  PRBool eof;
1113
  PRBool eof;
1108
  do {
1114
  do {
1109
    PRBool skip = PR_FALSE;
1115
    PRBool skip = PR_FALSE;
1110
    eof = !DecodeVideoFrame(skip, 0);
1116
    PRUint32 parsed, decoded;
1117
    eof = !DecodeVideoFrame(skip, 0, parsed, decoded);
1111
    {
1118
    {
1112
      MonitorAutoExit exitReaderMon(mMonitor);
1119
      MonitorAutoExit exitReaderMon(mMonitor);
1113
      MonitorAutoEnter decoderMon(mDecoder->GetMonitor());
1120
      MonitorAutoEnter decoderMon(mDecoder->GetMonitor());
1114
      if (mDecoder->GetDecodeState() == nsBuiltinDecoderStateMachine::DECODER_STATE_SHUTDOWN) {
1121
      if (mDecoder->GetDecodeState() == nsBuiltinDecoderStateMachine::DECODER_STATE_SHUTDOWN) {
1115
        return NS_ERROR_FAILURE;
1122
        return NS_ERROR_FAILURE;
1116
      }
1123
      }
1117
    }
1124
    }
1118
  } while (!eof &&
1125
  } while (!eof &&
(-)a/content/media/ogg/nsOggReader.h (-2 / +4 lines)
Line     Link Here 
 Lines 63-80   public: Link Here 
63
63
64
  virtual nsresult Init(nsBuiltinDecoderReader* aCloneDonor);
64
  virtual nsresult Init(nsBuiltinDecoderReader* aCloneDonor);
65
  virtual nsresult ResetDecode();
65
  virtual nsresult ResetDecode();
66
  virtual PRBool DecodeAudioData();
66
  virtual PRBool DecodeAudioData();
67
67
68
  // If the Theora granulepos has not been captured, it may read several packets
68
  // If the Theora granulepos has not been captured, it may read several packets
69
  // until one with a granulepos has been captured, to ensure that all packets
69
  // until one with a granulepos has been captured, to ensure that all packets
70
  // read have valid time info.  
70
  // read have valid time info.  
71
  virtual PRBool DecodeVideoFrame(PRBool &aKeyframeSkip,
71
  virtual PRBool DecodeVideoFrame(PRBool& aKeyframeSkip,
72
                                  PRInt64 aTimeThreshold);
72
                                  PRInt64 aTimeThreshold,
73
                                  PRUint32& aParsed,
74
                                  PRUint32& aDecoded);
73
75
74
  virtual VideoData* FindStartTime(PRInt64 aOffset,
76
  virtual VideoData* FindStartTime(PRInt64 aOffset,
75
                                   PRInt64& aOutStartTime);
77
                                   PRInt64& aOutStartTime);
76
78
77
  // Get the end time of aEndOffset. This is the playback position we'd reach
79
  // Get the end time of aEndOffset. This is the playback position we'd reach
78
  // after playback finished at aEndOffset.
80
  // after playback finished at aEndOffset.
79
  virtual PRInt64 FindEndTime(PRInt64 aEndOffset);
81
  virtual PRInt64 FindEndTime(PRInt64 aEndOffset);
80
82
(-)a/content/media/raw/nsRawReader.cpp (-2 / +9 lines)
Line     Link Here 
 Lines 163-180   PRBool nsRawReader::ReadFromStream(nsMed Link Here 
163
    aLength -= bytesRead;
163
    aLength -= bytesRead;
164
    aBuf += bytesRead;
164
    aBuf += bytesRead;
165
  }
165
  }
166
166
167
  return PR_TRUE;
167
  return PR_TRUE;
168
}
168
}
169
169
170
PRBool nsRawReader::DecodeVideoFrame(PRBool &aKeyframeSkip,
170
PRBool nsRawReader::DecodeVideoFrame(PRBool &aKeyframeSkip,
171
                                     PRInt64 aTimeThreshold)
171
                                     PRInt64 aTimeThreshold,
172
                                     PRUint32& aParsed,
173
                                     PRUint32& aDecoded)
172
{
174
{
175
  aParsed = aDecoded = 0;
173
  mozilla::MonitorAutoEnter autoEnter(mMonitor);
176
  mozilla::MonitorAutoEnter autoEnter(mMonitor);
174
  NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
177
  NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
175
               "Should be on state machine thread or decode thread.");
178
               "Should be on state machine thread or decode thread.");
176
179
177
  if (!mFrameSize)
180
  if (!mFrameSize)
178
    return PR_FALSE; // Metadata read failed.  We should refuse to play.
181
    return PR_FALSE; // Metadata read failed.  We should refuse to play.
179
182
180
  PRInt64 currentFrameTime = 1000 * mCurrentFrame / mFrameRate;
183
  PRInt64 currentFrameTime = 1000 * mCurrentFrame / mFrameRate;
 Lines 194-209   PRBool nsRawReader::DecodeVideoFrame(PRB Link Here 
194
        !(header.packetID == 0xFF && header.codecID == RAW_ID /* "YUV" */)) {
197
        !(header.packetID == 0xFF && header.codecID == RAW_ID /* "YUV" */)) {
195
      return PR_FALSE;
198
      return PR_FALSE;
196
    }
199
    }
197
200
198
    if (!ReadFromStream(stream, buffer, length)) {
201
    if (!ReadFromStream(stream, buffer, length)) {
199
      return PR_FALSE;
202
      return PR_FALSE;
200
    }
203
    }
201
204
205
    aParsed++;
206
202
    if (currentFrameTime >= aTimeThreshold)
207
    if (currentFrameTime >= aTimeThreshold)
203
      break;
208
      break;
204
209
205
    mCurrentFrame++;
210
    mCurrentFrame++;
206
    currentFrameTime += 1000.0 / mFrameRate;
211
    currentFrameTime += 1000.0 / mFrameRate;
207
  }
212
  }
208
213
209
  VideoData::YCbCrBuffer b;
214
  VideoData::YCbCrBuffer b;
 Lines 232-247   PRBool nsRawReader::DecodeVideoFrame(PRB Link Here 
232
                                   b,
237
                                   b,
233
                                   1, // In raw video every frame is a keyframe
238
                                   1, // In raw video every frame is a keyframe
234
                                   -1);
239
                                   -1);
235
  if (!v)
240
  if (!v)
236
    return PR_FALSE;
241
    return PR_FALSE;
237
242
238
  mVideoQueue.Push(v);
243
  mVideoQueue.Push(v);
239
  mCurrentFrame++;
244
  mCurrentFrame++;
245
  aDecoded++;
240
  currentFrameTime += 1000 / mFrameRate;
246
  currentFrameTime += 1000 / mFrameRate;
241
247
242
  return PR_TRUE;
248
  return PR_TRUE;
243
}
249
}
244
250
245
nsresult nsRawReader::Seek(PRInt64 aTime, PRInt64 aStartTime, PRInt64 aEndTime, PRInt64 aCurrentTime)
251
nsresult nsRawReader::Seek(PRInt64 aTime, PRInt64 aStartTime, PRInt64 aEndTime, PRInt64 aCurrentTime)
246
{
252
{
247
  mozilla::MonitorAutoEnter autoEnter(mMonitor);
253
  mozilla::MonitorAutoEnter autoEnter(mMonitor);
 Lines 264-280   nsresult nsRawReader::Seek(PRInt64 aTime Link Here 
264
270
265
  nsresult rv = stream->Seek(nsISeekableStream::NS_SEEK_SET, offset);
271
  nsresult rv = stream->Seek(nsISeekableStream::NS_SEEK_SET, offset);
266
  NS_ENSURE_SUCCESS(rv, rv);
272
  NS_ENSURE_SUCCESS(rv, rv);
267
273
268
  mVideoQueue.Erase();
274
  mVideoQueue.Erase();
269
275
270
  while(mVideoQueue.GetSize() == 0) {
276
  while(mVideoQueue.GetSize() == 0) {
271
    PRBool keyframeSkip = PR_FALSE;
277
    PRBool keyframeSkip = PR_FALSE;
272
    if (!DecodeVideoFrame(keyframeSkip, 0)) {
278
    PRUint32 parsed, decoded;
279
    if (!DecodeVideoFrame(keyframeSkip, 0, parsed, decoded)) {
273
      mCurrentFrame = frame;
280
      mCurrentFrame = frame;
274
      return NS_ERROR_FAILURE;
281
      return NS_ERROR_FAILURE;
275
    }
282
    }
276
283
277
    {
284
    {
278
      mozilla::MonitorAutoExit autoMonitorExit(mMonitor);
285
      mozilla::MonitorAutoExit autoMonitorExit(mMonitor);
279
      mozilla::MonitorAutoEnter autoMonitor(mDecoder->GetMonitor());
286
      mozilla::MonitorAutoEnter autoMonitor(mDecoder->GetMonitor());
280
      if (mDecoder->GetDecodeState() ==
287
      if (mDecoder->GetDecodeState() ==
(-)a/content/media/raw/nsRawReader.h (-2 / +4 lines)
Line     Link Here 
 Lines 92-109   class nsRawReader : public nsBuiltinDeco Link Here 
92
public:
92
public:
93
  nsRawReader(nsBuiltinDecoder* aDecoder);
93
  nsRawReader(nsBuiltinDecoder* aDecoder);
94
  ~nsRawReader();
94
  ~nsRawReader();
95
95
96
  virtual nsresult Init(nsBuiltinDecoderReader* aCloneDonor);
96
  virtual nsresult Init(nsBuiltinDecoderReader* aCloneDonor);
97
  virtual nsresult ResetDecode();
97
  virtual nsresult ResetDecode();
98
  virtual PRBool DecodeAudioData();
98
  virtual PRBool DecodeAudioData();
99
99
100
  virtual PRBool DecodeVideoFrame(PRBool &aKeyframeSkip,
100
  virtual PRBool DecodeVideoFrame(PRBool& aKeyframeSkip,
101
                                  PRInt64 aTimeThreshold);
101
                                  PRInt64 aTimeThreshold,
102
                                  PRUint32& aParsed,
103
                                  PRUint32& aDecoded);
102
104
103
  virtual PRBool HasAudio()
105
  virtual PRBool HasAudio()
104
  {
106
  {
105
    return PR_FALSE;
107
    return PR_FALSE;
106
  }
108
  }
107
109
108
  virtual PRBool HasVideo()
110
  virtual PRBool HasVideo()
109
  {
111
  {
(-)a/content/media/webm/nsWebMReader.cpp (-4 / +13 lines)
Line     Link Here 
 Lines 538-556   PRBool nsWebMReader::DecodeAudioData() Link Here 
538
  if (!holder) {
538
  if (!holder) {
539
    mAudioQueue.Finish();
539
    mAudioQueue.Finish();
540
    return PR_FALSE;
540
    return PR_FALSE;
541
  }
541
  }
542
542
543
  return DecodeAudioPacket(holder->mPacket, holder->mOffset);
543
  return DecodeAudioPacket(holder->mPacket, holder->mOffset);
544
}
544
}
545
545
546
PRBool nsWebMReader::DecodeVideoFrame(PRBool &aKeyframeSkip,
546
PRBool nsWebMReader::DecodeVideoFrame(PRBool& aKeyframeSkip,
547
                                      PRInt64 aTimeThreshold)
547
                                      PRInt64 aTimeThreshold,
548
                                      PRUint32& aParsed,
549
                                      PRUint32& aDecoded)
548
{
550
{
551
  aParsed = aDecoded = 0;
549
  MonitorAutoEnter mon(mMonitor);
552
  MonitorAutoEnter mon(mMonitor);
550
  NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
553
  NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
551
               "Should be on state machine or decode thread.");
554
               "Should be on state machine or decode thread.");
552
555
553
  nsAutoRef<NesteggPacketHolder> holder(NextPacket(VIDEO));
556
  nsAutoRef<NesteggPacketHolder> holder(NextPacket(VIDEO));
554
  if (!holder) {
557
  if (!holder) {
555
    mVideoQueue.Finish();
558
    mVideoQueue.Finish();
556
    return PR_FALSE;
559
    return PR_FALSE;
 Lines 611-648   PRBool nsWebMReader::DecodeVideoFrame(PR Link Here 
611
    }
614
    }
612
615
613
    vpx_codec_stream_info_t si;
616
    vpx_codec_stream_info_t si;
614
    memset(&si, 0, sizeof(si));
617
    memset(&si, 0, sizeof(si));
615
    si.sz = sizeof(si);
618
    si.sz = sizeof(si);
616
    vpx_codec_peek_stream_info(&vpx_codec_vp8_dx_algo, data, length, &si);
619
    vpx_codec_peek_stream_info(&vpx_codec_vp8_dx_algo, data, length, &si);
617
    if ((aKeyframeSkip && !si.is_kf) || (aKeyframeSkip && si.is_kf && tstamp_ms < aTimeThreshold)) {
620
    if ((aKeyframeSkip && !si.is_kf) || (aKeyframeSkip && si.is_kf && tstamp_ms < aTimeThreshold)) {
618
      aKeyframeSkip = PR_TRUE;
621
      aKeyframeSkip = PR_TRUE;
622
      aParsed++; // Assume 1 frame per chunk.
619
      break;
623
      break;
620
    }
624
    }
621
625
622
    if (aKeyframeSkip && si.is_kf) {
626
    if (aKeyframeSkip && si.is_kf) {
623
      aKeyframeSkip = PR_FALSE;
627
      aKeyframeSkip = PR_FALSE;
624
    }
628
    }
625
629
626
    if(vpx_codec_decode(&mVP8, data, length, NULL, 0)) {
630
    if (vpx_codec_decode(&mVP8, data, length, NULL, 0)) {
627
      return PR_FALSE;
631
      return PR_FALSE;
628
    }
632
    }
629
633
630
    // If the timestamp of the video frame is less than
634
    // If the timestamp of the video frame is less than
631
    // the time threshold required then it is not added
635
    // the time threshold required then it is not added
632
    // to the video queue and won't be displayed.
636
    // to the video queue and won't be displayed.
633
    if (tstamp_ms < aTimeThreshold) {
637
    if (tstamp_ms < aTimeThreshold) {
638
      aParsed++; // Assume 1 frame per chunk.
634
      continue;
639
      continue;
635
    }
640
    }
636
641
637
    vpx_codec_iter_t  iter = NULL;
642
    vpx_codec_iter_t  iter = NULL;
638
    vpx_image_t      *img;
643
    vpx_image_t      *img;
639
644
640
    while((img = vpx_codec_get_frame(&mVP8, &iter))) {
645
    while ((img = vpx_codec_get_frame(&mVP8, &iter))) {
641
      NS_ASSERTION(mInfo.mPicture.width == static_cast<PRInt32>(img->d_w), 
646
      NS_ASSERTION(mInfo.mPicture.width == static_cast<PRInt32>(img->d_w), 
642
                   "WebM picture width from header does not match decoded frame");
647
                   "WebM picture width from header does not match decoded frame");
643
      NS_ASSERTION(mInfo.mPicture.height == static_cast<PRInt32>(img->d_h),
648
      NS_ASSERTION(mInfo.mPicture.height == static_cast<PRInt32>(img->d_h),
644
                   "WebM picture height from header does not match decoded frame");
649
                   "WebM picture height from header does not match decoded frame");
645
      NS_ASSERTION(img->fmt == IMG_FMT_I420, "WebM image format is not I420");
650
      NS_ASSERTION(img->fmt == IMG_FMT_I420, "WebM image format is not I420");
646
651
647
      // Chroma shifts are rounded down as per the decoding examples in the VP8 SDK
652
      // Chroma shifts are rounded down as per the decoding examples in the VP8 SDK
648
      VideoData::YCbCrBuffer b;
653
      VideoData::YCbCrBuffer b;
 Lines 667-682   PRBool nsWebMReader::DecodeVideoFrame(PR Link Here 
667
                                       tstamp_ms,
672
                                       tstamp_ms,
668
                                       next_tstamp / NS_PER_MS,
673
                                       next_tstamp / NS_PER_MS,
669
                                       b,
674
                                       b,
670
                                       si.is_kf,
675
                                       si.is_kf,
671
                                       -1);
676
                                       -1);
672
      if (!v) {
677
      if (!v) {
673
        return PR_FALSE;
678
        return PR_FALSE;
674
      }
679
      }
680
      aParsed++;
681
      aDecoded++;
682
      NS_ASSERTION(aDecoded <= aParsed,
683
        "Expect only 1 frame per chunk per packet in WebM...");
675
      mVideoQueue.Push(v);
684
      mVideoQueue.Push(v);
676
    }
685
    }
677
  }
686
  }
678
687
679
  return PR_TRUE;
688
  return PR_TRUE;
680
}
689
}
681
690
682
nsresult nsWebMReader::Seek(PRInt64 aTarget, PRInt64 aStartTime, PRInt64 aEndTime,
691
nsresult nsWebMReader::Seek(PRInt64 aTarget, PRInt64 aStartTime, PRInt64 aEndTime,
(-)a/content/media/webm/nsWebMReader.h (-2 / +4 lines)
Line     Link Here 
 Lines 133-150   public: Link Here 
133
133
134
  virtual nsresult Init(nsBuiltinDecoderReader* aCloneDonor);
134
  virtual nsresult Init(nsBuiltinDecoderReader* aCloneDonor);
135
  virtual nsresult ResetDecode();
135
  virtual nsresult ResetDecode();
136
  virtual PRBool DecodeAudioData();
136
  virtual PRBool DecodeAudioData();
137
137
138
  // If the Theora granulepos has not been captured, it may read several packets
138
  // If the Theora granulepos has not been captured, it may read several packets
139
  // until one with a granulepos has been captured, to ensure that all packets
139
  // until one with a granulepos has been captured, to ensure that all packets
140
  // read have valid time info.  
140
  // read have valid time info.  
141
  virtual PRBool DecodeVideoFrame(PRBool &aKeyframeSkip,
141
  virtual PRBool DecodeVideoFrame(PRBool& aKeyframeSkip,
142
                                  PRInt64 aTimeThreshold);
142
                                  PRInt64 aTimeThreshold,
143
                                  PRUint32& aParsed,
144
                                  PRUint32& aDecoded);
143
145
144
  virtual PRBool HasAudio()
146
  virtual PRBool HasAudio()
145
  {
147
  {
146
    mozilla::MonitorAutoEnter mon(mMonitor);
148
    mozilla::MonitorAutoEnter mon(mMonitor);
147
    return mHasAudio;
149
    return mHasAudio;
148
  }
150
  }
149
151
150
  virtual PRBool HasVideo()
152
  virtual PRBool HasVideo()
(-)a/dom/interfaces/html/nsIDOMHTMLVideoElement.idl (-1 / +20 lines)
Line     Link Here 
 Lines 43-60    Link Here 
43
 * <video> element.
43
 * <video> element.
44
 *
44
 *
45
 * For more information on this interface, please see
45
 * For more information on this interface, please see
46
 * http://www.whatwg.org/specs/web-apps/current-work/#video
46
 * http://www.whatwg.org/specs/web-apps/current-work/#video
47
 *
47
 *
48
 * @status UNDER_DEVELOPMENT
48
 * @status UNDER_DEVELOPMENT
49
 */
49
 */
50
50
51
[scriptable, uuid(edf468dc-42eb-4494-920b-56a315172640)]
51
[scriptable, uuid(e1f52aa5-9962-4019-b7b3-af3aee6e4d48)]
52
interface nsIDOMHTMLVideoElement : nsIDOMHTMLMediaElement
52
interface nsIDOMHTMLVideoElement : nsIDOMHTMLMediaElement
53
{
53
{
54
           attribute long width; 
54
           attribute long width; 
55
           attribute long height;
55
           attribute long height;
56
  readonly attribute unsigned long videoWidth;
56
  readonly attribute unsigned long videoWidth;
57
  readonly attribute unsigned long videoHeight;
57
  readonly attribute unsigned long videoHeight;
58
           attribute DOMString poster;
58
           attribute DOMString poster;
59
           
60
  // A count of the number of video frames that have demuxed from the media
61
  // resource. If we were playing perfectly, we'd be able to paint this many
62
  // frames.
63
  readonly attribute unsigned long mozParsedFrames;
64
65
  // A count of the number of frames that have been decoded. We may drop
66
  // frames if the decode is taking too much time.
67
  readonly attribute unsigned long mozDecodedFrames;
68
69
  // A count of the number of frames that have been presented to the rendering
70
  // pipeline. We may drop frames if they arrive late at the renderer.
71
  readonly attribute unsigned long mozPresentedFrames;
72
  
73
  // Number of presented frames which were drawn on screen.
74
  readonly attribute unsigned long mozPaintedFrames;
75
  
76
  // Time which the last painted video frame was late by, in seconds.
77
  readonly attribute float mozFrameDelay;
59
};
78
};
60
79
(-)a/gfx/layers/ImageLayers.h (-2 / +52 lines)
Line     Link Here 
 Lines 34-52    Link Here 
34
 * the terms of any one of the MPL, the GPL or the LGPL.
34
 * the terms of any one of the MPL, the GPL or the LGPL.
35
 *
35
 *
36
 * ***** END LICENSE BLOCK ***** */
36
 * ***** END LICENSE BLOCK ***** */
37
37
38
#ifndef GFX_IMAGELAYER_H
38
#ifndef GFX_IMAGELAYER_H
39
#define GFX_IMAGELAYER_H
39
#define GFX_IMAGELAYER_H
40
40
41
#include "Layers.h"
41
#include "Layers.h"
42
#include "mozilla/Monitor.h"
42
43
43
#include "gfxPattern.h"
44
#include "gfxPattern.h"
44
#include "nsThreadUtils.h"
45
#include "nsThreadUtils.h"
46
#include "mozilla/TimeStamp.h"
45
47
46
namespace mozilla {
48
namespace mozilla {
47
namespace layers {
49
namespace layers {
48
50
49
/**
51
/**
50
 * A class representing a buffer of pixel data. The data can be in one
52
 * A class representing a buffer of pixel data. The data can be in one
51
 * of various formats including YCbCr.
53
 * of various formats including YCbCr.
52
 * 
54
 * 
 Lines 108-125   protected: Link Here 
108
 * we need a separate class here is that ImageLayers aren't threadsafe
110
 * we need a separate class here is that ImageLayers aren't threadsafe
109
 * (because layers can only be used on the main thread) and we want to
111
 * (because layers can only be used on the main thread) and we want to
110
 * be able to set the current Image from any thread, to facilitate
112
 * be able to set the current Image from any thread, to facilitate
111
 * video playback without involving the main thread, for example.
113
 * video playback without involving the main thread, for example.
112
 */
114
 */
113
class THEBES_API ImageContainer {
115
class THEBES_API ImageContainer {
114
  THEBES_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer)
116
  THEBES_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainer)
115
117
118
  typedef mozilla::Monitor Monitor;
119
116
public:
120
public:
117
  ImageContainer() {}
121
  ImageContainer() : mTimeMonitor("ImageContainer"), mImagePainted(PR_FALSE) {}
118
  virtual ~ImageContainer() {}
122
  virtual ~ImageContainer() {}
119
123
120
  /**
124
  /**
121
   * Create an Image in one of the given formats.
125
   * Create an Image in one of the given formats.
122
   * Picks the "best" format from the list and creates an Image of that
126
   * Picks the "best" format from the list and creates an Image of that
123
   * format.
127
   * format.
124
   * Returns null if this backend does not support any of the formats.
128
   * Returns null if this backend does not support any of the formats.
125
   */
129
   */
 Lines 182-201   public: Link Here 
182
186
183
  /**
187
  /**
184
   * Sets a size that the image is expected to be rendered at.
188
   * Sets a size that the image is expected to be rendered at.
185
   * This is a hint for image backends to optimize scaling.
189
   * This is a hint for image backends to optimize scaling.
186
   * Default implementation in this class is to ignore the hint.
190
   * Default implementation in this class is to ignore the hint.
187
   */
191
   */
188
  virtual void SetScaleHint(const gfxIntSize& /* aScaleHint */) { }
192
  virtual void SetScaleHint(const gfxIntSize& /* aScaleHint */) { }
189
193
194
  /**
195
   * Returns the duration which the paint was late by, if a paint target
196
   * was specified.
197
   */
198
  TimeDuration GetPaintDelay();
199
200
  /**
201
   * Notifies the ImageLayer that it's been painted, so it can calculate
202
   * the delay between the target paint time, and the achieved paint time.
203
   */
204
  void NotifyPainted(TimeStamp aPaintTime);
205
206
  /**
207
   * Sets the time at which we'd like the contained to be image painted.
208
   */
209
  void SetPaintTarget(TimeStamp aTime);
210
211
  /**
212
   * Returns the target time at which we'd like the contained image
213
   * to be painted, as previously set by SetPaintTarget().
214
   */
215
  TimeStamp GetPaintTarget();
216
190
protected:
217
protected:
191
  LayerManager* mManager;
218
  LayerManager* mManager;
192
219
193
  ImageContainer(LayerManager* aManager) : mManager(aManager) {}
220
  /**
221
   * Protects acces to mTargetTime, mDelay, and mImagePainted.
222
   */
223
  Monitor mTimeMonitor;
224
225
  /**
226
   * Time at which we aim to paint the image. Set by SetPaintTarget(). This
227
   * is typically set on video frames in the video decoder.
228
   */
229
  TimeStamp mTargetTime;
230
231
  /**
232
   * Duration by which the paint was late by. This is only valid if a target
233
   * paint time was specified, and if the image has actually been painted.
234
   */
235
  TimeDuration mDelay;
236
237
  /**
238
   * Set to PR_TRUE if the currently contained image has been painted at
239
   * least once.
240
   */
241
  PRBool mImagePainted;
242
243
  ImageContainer(LayerManager* aManager) : mManager(aManager), mTimeMonitor("ImageContainer") {}
194
};
244
};
195
245
196
/**
246
/**
197
 * A Layer which renders an Image.
247
 * A Layer which renders an Image.
198
 */
248
 */
199
class THEBES_API ImageLayer : public Layer {
249
class THEBES_API ImageLayer : public Layer {
200
public:
250
public:
201
  /**
251
  /**
(-)a/gfx/layers/Layers.cpp (+49 lines)
Line     Link Here 
 Lines 180-195   namespace layers { Link Here 
180
already_AddRefed<gfxASurface>
180
already_AddRefed<gfxASurface>
181
LayerManager::CreateOptimalSurface(const gfxIntSize &aSize,
181
LayerManager::CreateOptimalSurface(const gfxIntSize &aSize,
182
                                   gfxASurface::gfxImageFormat aFormat)
182
                                   gfxASurface::gfxImageFormat aFormat)
183
{
183
{
184
  return gfxPlatform::GetPlatform()->
184
  return gfxPlatform::GetPlatform()->
185
    CreateOffscreenSurface(aSize, gfxASurface::ContentFromFormat(aFormat));
185
    CreateOffscreenSurface(aSize, gfxASurface::ContentFromFormat(aFormat));
186
}
186
}
187
187
188
static void NotifyPainted(Layer* aLayer, TimeStamp aTimeStamp) {
189
  NS_ASSERTION(aLayer, "Must have specified a non-null layer");
190
  NS_ASSERTION(!aTimeStamp.IsNull(), "Must have a valid timestamp");
191
  if (aLayer->GetType() == Layer::TYPE_IMAGE) {
192
    ImageLayer* imgLayer = static_cast<ImageLayer*>(aLayer);
193
    ImageContainer* container = imgLayer->GetContainer();
194
    container->NotifyPainted(aTimeStamp);
195
  }
196
  Layer* child = aLayer->GetFirstChild();
197
  for (; child; child = child->GetNextSibling()) {
198
    NotifyPainted(child, aTimeStamp);
199
  }
200
}
201
202
void
203
LayerManager::NotifyPainted()
204
{
205
  if (mRoot)
206
    ::NotifyPainted(mRoot, TimeStamp::Now());
207
}
208
188
//--------------------------------------------------
209
//--------------------------------------------------
189
// Layer
210
// Layer
190
211
191
PRBool
212
PRBool
192
Layer::CanUseOpaqueSurface()
213
Layer::CanUseOpaqueSurface()
193
{
214
{
194
  // If the visible content in the layer is opaque, there is no need
215
  // If the visible content in the layer is opaque, there is no need
195
  // for an alpha channel.
216
  // for an alpha channel.
 Lines 644-653   LayerManager::PrintInfo(nsACString& aTo, Link Here 
644
665
645
/*static*/ void LayerManager::InitLog() {}
666
/*static*/ void LayerManager::InitLog() {}
646
/*static*/ bool LayerManager::IsLogEnabled() { return false; }
667
/*static*/ bool LayerManager::IsLogEnabled() { return false; }
647
668
648
#endif // MOZ_LAYERS_HAVE_LOG
669
#endif // MOZ_LAYERS_HAVE_LOG
649
670
650
PRLogModuleInfo* LayerManager::sLog;
671
PRLogModuleInfo* LayerManager::sLog;
651
672
673
TimeDuration ImageContainer::GetPaintDelay() {
674
  MonitorAutoEnter mon(mTimeMonitor);
675
  return mDelay;
676
}
677
678
void ImageContainer::NotifyPainted(TimeStamp aPaintTime) {
679
  MonitorAutoEnter mon(mTimeMonitor);
680
  if (mImagePainted || mTargetTime.IsNull() || mTargetTime > aPaintTime)
681
    return;
682
  mDelay = aPaintTime - mTargetTime;
683
  // Remember that we've painted this image, so that we won't recalculate
684
  // and assume a larger delay if we paint this image again.
685
  mImagePainted = PR_TRUE;
686
}
687
688
TimeStamp ImageContainer::GetPaintTarget() {
689
  MonitorAutoEnter mon(mTimeMonitor);
690
  return mTargetTime;
691
}
692
693
void ImageContainer::SetPaintTarget(TimeStamp aTime) {
694
  MonitorAutoEnter mon(mTimeMonitor);
695
  mTargetTime = aTime;
696
  // Reset our "has been painted" flag, so we know to recalculate the paint
697
  // delay the first time this frame is painted.
698
  mImagePainted = PR_FALSE;
699
}
700
652
} // namespace layers 
701
} // namespace layers 
653
} // namespace mozilla
702
} // namespace mozilla
(-)a/gfx/layers/Layers.h (+7 lines)
Line     Link Here 
 Lines 418-433   public: Link Here 
418
   * Log information about just this layer manager itself to the NSPR
418
   * Log information about just this layer manager itself to the NSPR
419
   * log (if enabled for "Layers").
419
   * log (if enabled for "Layers").
420
   */
420
   */
421
  void LogSelf(const char* aPrefix="");
421
  void LogSelf(const char* aPrefix="");
422
422
423
  static bool IsLogEnabled();
423
  static bool IsLogEnabled();
424
  static PRLogModuleInfo* GetLog() { return sLog; }
424
  static PRLogModuleInfo* GetLog() { return sLog; }
425
425
426
  /**
427
   * Notifies all ImageLayers in the layer tree that they've been painted, so
428
   * that they can record paint-delay statistics. Call this at the end of every
429
   * EndTransaction() implementation.
430
   */
431
  void NotifyPainted();
432
426
protected:
433
protected:
427
  nsRefPtr<Layer> mRoot;
434
  nsRefPtr<Layer> mRoot;
428
  LayerUserDataSet mUserData;
435
  LayerUserDataSet mUserData;
429
  PRPackedBool mDestroyed;
436
  PRPackedBool mDestroyed;
430
  PRPackedBool mSnapEffectiveTransforms;
437
  PRPackedBool mSnapEffectiveTransforms;
431
438
432
  // Print interesting information about this into aTo.  Internally
439
  // Print interesting information about this into aTo.  Internally
433
  // used to implement Dump*() and Log*().
440
  // used to implement Dump*() and Log*().
(-)a/gfx/layers/basic/BasicLayers.cpp (+4 lines)
Line     Link Here 
 Lines 1230-1245   BasicLayerManager::EndTransaction(DrawTh Link Here 
1230
    if (useDoubleBuffering) {
1230
    if (useDoubleBuffering) {
1231
      finalTarget->SetOperator(gfxContext::OPERATOR_SOURCE);
1231
      finalTarget->SetOperator(gfxContext::OPERATOR_SOURCE);
1232
      PopGroupWithCachedSurface(finalTarget, cachedSurfaceOffset);
1232
      PopGroupWithCachedSurface(finalTarget, cachedSurfaceOffset);
1233
    }
1233
    }
1234
1234
1235
    mTarget = nsnull;
1235
    mTarget = nsnull;
1236
  }
1236
  }
1237
1237
1238
  NotifyPainted();
1239
1238
#ifdef MOZ_LAYERS_HAVE_LOG
1240
#ifdef MOZ_LAYERS_HAVE_LOG
1239
  Log();
1241
  Log();
1240
  MOZ_LAYERS_LOG(("]----- EndTransaction"));
1242
  MOZ_LAYERS_LOG(("]----- EndTransaction"));
1241
#endif
1243
#endif
1242
1244
1243
#ifdef DEBUG
1245
#ifdef DEBUG
1244
  mPhase = PHASE_NONE;
1246
  mPhase = PHASE_NONE;
1245
#endif
1247
#endif
 Lines 2580-2595   BasicShadowLayerManager::EndTransaction( Link Here 
2580
      default:
2582
      default:
2581
        NS_RUNTIMEABORT("not reached");
2583
        NS_RUNTIMEABORT("not reached");
2582
      }
2584
      }
2583
    }
2585
    }
2584
  } else if (HasShadowManager()) {
2586
  } else if (HasShadowManager()) {
2585
    NS_WARNING("failed to forward Layers transaction");
2587
    NS_WARNING("failed to forward Layers transaction");
2586
  }
2588
  }
2587
2589
2590
  NotifyPainted();
2591
2588
#ifdef DEBUG
2592
#ifdef DEBUG
2589
  mPhase = PHASE_NONE;
2593
  mPhase = PHASE_NONE;
2590
#endif
2594
#endif
2591
2595
2592
  // this may result in Layers being deleted, which results in
2596
  // this may result in Layers being deleted, which results in
2593
  // PLayer::Send__delete__() and DeallocShmem()
2597
  // PLayer::Send__delete__() and DeallocShmem()
2594
  mKeepAlive.Clear();
2598
  mKeepAlive.Clear();
2595
}
2599
}
(-)a/gfx/layers/d3d10/LayerManagerD3D10.cpp (+1 lines)
Line     Link Here 
 Lines 240-255   LayerManagerD3D10::EndTransaction(DrawTh Link Here 
240
  // The results of our drawing always go directly into a pixel buffer,
240
  // The results of our drawing always go directly into a pixel buffer,
241
  // so we don't need to pass any global transform here.
241
  // so we don't need to pass any global transform here.
242
  mRoot->ComputeEffectiveTransforms(gfx3DMatrix());
242
  mRoot->ComputeEffectiveTransforms(gfx3DMatrix());
243
243
244
  Render();
244
  Render();
245
  mCurrentCallbackInfo.Callback = nsnull;
245
  mCurrentCallbackInfo.Callback = nsnull;
246
  mCurrentCallbackInfo.CallbackData = nsnull;
246
  mCurrentCallbackInfo.CallbackData = nsnull;
247
  mTarget = nsnull;
247
  mTarget = nsnull;
248
  NotifyPainted();
248
}
249
}
249
250
250
already_AddRefed<ThebesLayer>
251
already_AddRefed<ThebesLayer>
251
LayerManagerD3D10::CreateThebesLayer()
252
LayerManagerD3D10::CreateThebesLayer()
252
{
253
{
253
  nsRefPtr<ThebesLayer> layer = new ThebesLayerD3D10(this);
254
  nsRefPtr<ThebesLayer> layer = new ThebesLayerD3D10(this);
254
  return layer.forget();
255
  return layer.forget();
255
}
256
}
(-)a/gfx/layers/d3d9/LayerManagerD3D9.cpp (+2 lines)
Line     Link Here 
 Lines 161-176   LayerManagerD3D9::EndTransaction(DrawThe Link Here 
161
  mRoot->ComputeEffectiveTransforms(gfx3DMatrix());
161
  mRoot->ComputeEffectiveTransforms(gfx3DMatrix());
162
162
163
  Render();
163
  Render();
164
  /* Clean this out for sanity */
164
  /* Clean this out for sanity */
165
  mCurrentCallbackInfo.Callback = NULL;
165
  mCurrentCallbackInfo.Callback = NULL;
166
  mCurrentCallbackInfo.CallbackData = NULL;
166
  mCurrentCallbackInfo.CallbackData = NULL;
167
  // Clear mTarget, next transaction could have no target
167
  // Clear mTarget, next transaction could have no target
168
  mTarget = NULL;
168
  mTarget = NULL;
169
170
  NotifyPainted();
169
}
171
}
170
172
171
void
173
void
172
LayerManagerD3D9::SetRoot(Layer *aLayer)
174
LayerManagerD3D9::SetRoot(Layer *aLayer)
173
{
175
{
174
  mRoot = aLayer;
176
  mRoot = aLayer;
175
}
177
}
176
178
(-)a/gfx/layers/opengl/LayerManagerOGL.cpp (+2 lines)
Line     Link Here 
 Lines 416-431   LayerManagerOGL::EndTransaction(DrawTheb Link Here 
416
    Render();
416
    Render();
417
  }
417
  }
418
418
419
  mThebesLayerCallback = nsnull;
419
  mThebesLayerCallback = nsnull;
420
  mThebesLayerCallbackData = nsnull;
420
  mThebesLayerCallbackData = nsnull;
421
421
422
  mTarget = NULL;
422
  mTarget = NULL;
423
423
424
  NotifyPainted();
425
424
#ifdef MOZ_LAYERS_HAVE_LOG
426
#ifdef MOZ_LAYERS_HAVE_LOG
425
  Log();
427
  Log();
426
  MOZ_LAYERS_LOG(("]----- EndTransaction"));
428
  MOZ_LAYERS_LOG(("]----- EndTransaction"));
427
#endif
429
#endif
428
}
430
}
429
431
430
already_AddRefed<ThebesLayer>
432
already_AddRefed<ThebesLayer>
431
LayerManagerOGL::CreateThebesLayer()
433
LayerManagerOGL::CreateThebesLayer()
(-)a/layout/generic/nsVideoFrame.cpp (+13 lines)
Line     Link Here 
 Lines 266-281   nsVideoFrame::BuildLayer(nsDisplayListBu Link Here 
266
266
267
  layer->SetContainer(container);
267
  layer->SetContainer(container);
268
  layer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this));
268
  layer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this));
269
  // Set a transform on the layer to draw the video in the right place
269
  // Set a transform on the layer to draw the video in the right place
270
  gfxMatrix transform;
270
  gfxMatrix transform;
271
  transform.Translate(r.pos);
271
  transform.Translate(r.pos);
272
  transform.Scale(r.Width()/frameSize.width, r.Height()/frameSize.height);
272
  transform.Scale(r.Width()/frameSize.width, r.Height()/frameSize.height);
273
  layer->SetTransform(gfx3DMatrix::From2D(transform));
273
  layer->SetTransform(gfx3DMatrix::From2D(transform));
274
275
  if (HasVideoElement()) {
276
    TimeStamp target = container->GetPaintTarget();
277
    if (!target.IsNull() &&
278
        (mLastPaintedTarget.IsNull() || target != mLastPaintedTarget))
279
    {
280
      // This is the first time we've painted this frame, count it.
281
      mLastPaintedTarget = target;
282
      nsHTMLVideoElement* element = static_cast<nsHTMLVideoElement*>(GetContent());
283
      element->NotifyPaintedFrame();
284
    }
285
  }
286
274
  nsRefPtr<Layer> result = layer.forget();
287
  nsRefPtr<Layer> result = layer.forget();
275
  return result.forget();
288
  return result.forget();
276
}
289
}
277
290
278
NS_IMETHODIMP
291
NS_IMETHODIMP
279
nsVideoFrame::Reflow(nsPresContext*           aPresContext,
292
nsVideoFrame::Reflow(nsPresContext*           aPresContext,
280
                     nsHTMLReflowMetrics&     aMetrics,
293
                     nsHTMLReflowMetrics&     aMetrics,
281
                     const nsHTMLReflowState& aReflowState,
294
                     const nsHTMLReflowState& aReflowState,
(-)a/layout/generic/nsVideoFrame.h (+4 lines)
Line     Link Here 
 Lines 56-71   class nsDisplayItem; Link Here 
56
56
57
nsIFrame* NS_NewVideoFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
57
nsIFrame* NS_NewVideoFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
58
58
59
class nsVideoFrame : public nsContainerFrame, public nsIAnonymousContentCreator
59
class nsVideoFrame : public nsContainerFrame, public nsIAnonymousContentCreator
60
{
60
{
61
public:
61
public:
62
  typedef mozilla::layers::Layer Layer;
62
  typedef mozilla::layers::Layer Layer;
63
  typedef mozilla::layers::LayerManager LayerManager;
63
  typedef mozilla::layers::LayerManager LayerManager;
64
  typedef mozilla::TimeStamp TimeStamp;
64
65
65
  nsVideoFrame(nsStyleContext* aContext);
66
  nsVideoFrame(nsStyleContext* aContext);
66
67
67
  NS_DECL_QUERYFRAME
68
  NS_DECL_QUERYFRAME
68
  NS_DECL_FRAMEARENA_HELPERS
69
  NS_DECL_FRAMEARENA_HELPERS
69
70
70
  NS_IMETHOD BuildDisplayList(nsDisplayListBuilder*   aBuilder,
71
  NS_IMETHOD BuildDisplayList(nsDisplayListBuilder*   aBuilder,
71
                              const nsRect&           aDirtyRect,
72
                              const nsRect&           aDirtyRect,
 Lines 141-151   protected: Link Here 
141
142
142
  nsMargin mBorderPadding;
143
  nsMargin mBorderPadding;
143
  
144
  
144
  // Anonymous child which is bound via XBL to the video controls.
145
  // Anonymous child which is bound via XBL to the video controls.
145
  nsCOMPtr<nsIContent> mVideoControls;
146
  nsCOMPtr<nsIContent> mVideoControls;
146
  
147
  
147
  // Anonymous child which is the image element of the poster frame.
148
  // Anonymous child which is the image element of the poster frame.
148
  nsCOMPtr<nsIContent> mPosterImage;
149
  nsCOMPtr<nsIContent> mPosterImage;
150
151
  // Target timestamp of the last frame we painted.
152
  TimeStamp mLastPaintedTarget;
149
};
153
};
150
154
151
#endif /* nsVideoFrame_h___ */
155
#endif /* nsVideoFrame_h___ */

Return to bug 580531