Bug 910897 - Use MP3FrameParser in DirectShow in order to calculate the duration. r=padenot

This commit is contained in:
Chris Pearce 2013-09-10 12:45:33 +12:00
parent c3c6d3fde3
commit bd28d7d80f
10 changed files with 71 additions and 13 deletions

View File

@ -75,7 +75,7 @@ public:
// Sets the duration of the media in microseconds. The MediaDecoder // Sets the duration of the media in microseconds. The MediaDecoder
// fires a durationchange event to its owner (e.g., an HTML audio // fires a durationchange event to its owner (e.g., an HTML audio
// tag). // tag).
virtual void UpdateMediaDuration(int64_t aDuration) = 0; virtual void UpdateEstimatedMediaDuration(int64_t aDuration) = 0;
// Set the media as being seekable or not. // Set the media as being seekable or not.
virtual void SetMediaSeekable(bool aMediaSeekable) = 0; virtual void SetMediaSeekable(bool aMediaSeekable) = 0;

View File

@ -109,7 +109,7 @@ BufferDecoder::SetMediaDuration(int64_t aDuration)
} }
void void
BufferDecoder::UpdateMediaDuration(int64_t aDuration) BufferDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
{ {
// ignore // ignore
} }

View File

@ -50,7 +50,7 @@ public:
void SetMediaDuration(int64_t aDuration) MOZ_OVERRIDE; void SetMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
void UpdateMediaDuration(int64_t aDuration) MOZ_OVERRIDE; void UpdateEstimatedMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
void SetMediaSeekable(bool aMediaSeekable) MOZ_OVERRIDE; void SetMediaSeekable(bool aMediaSeekable) MOZ_OVERRIDE;

View File

@ -1272,10 +1272,14 @@ void MediaDecoder::SetMediaDuration(int64_t aDuration)
GetStateMachine()->SetDuration(aDuration); GetStateMachine()->SetDuration(aDuration);
} }
void MediaDecoder::UpdateMediaDuration(int64_t aDuration) void MediaDecoder::UpdateEstimatedMediaDuration(int64_t aDuration)
{ {
MOZ_ASSERT(NS_IsMainThread());
if (mPlayState <= PLAY_STATE_LOADING) {
return;
}
NS_ENSURE_TRUE_VOID(GetStateMachine()); NS_ENSURE_TRUE_VOID(GetStateMachine());
GetStateMachine()->UpdateDuration(aDuration); GetStateMachine()->UpdateEstimatedDuration(aDuration);
} }
void MediaDecoder::SetMediaSeekable(bool aMediaSeekable) { void MediaDecoder::SetMediaSeekable(bool aMediaSeekable) {

View File

@ -497,8 +497,18 @@ public:
// from a content header. Must be called from the main thread only. // from a content header. Must be called from the main thread only.
virtual void SetDuration(double aDuration); virtual void SetDuration(double aDuration);
// Sets the initial duration of the media. Called while the media metadata
// is being read and the decode is being setup.
void SetMediaDuration(int64_t aDuration) MOZ_OVERRIDE; void SetMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
void UpdateMediaDuration(int64_t aDuration) MOZ_OVERRIDE; // Updates the media duration. This is called while the media is being
// played, calls before the media has reached loaded metadata are ignored.
// The duration is assumed to be an estimate, and so a degree of
// instability is expected; if the incoming duration is not significantly
// different from the existing duration, the change request is ignored.
// If the incoming duration is significantly different, the duration is
// changed, this causes a durationchanged event to fire to the media
// element.
void UpdateEstimatedMediaDuration(int64_t aDuration) MOZ_OVERRIDE;
// Set a flag indicating whether seeking is supported // Set a flag indicating whether seeking is supported
virtual void SetMediaSeekable(bool aMediaSeekable) MOZ_OVERRIDE; virtual void SetMediaSeekable(bool aMediaSeekable) MOZ_OVERRIDE;

View File

@ -118,6 +118,12 @@ PR_STATIC_ASSERT(QUICK_BUFFERING_LOW_DATA_USECS <= AMPLE_AUDIO_USECS);
// This value has been chosen empirically. // This value has been chosen empirically.
static const uint32_t AUDIOSTREAM_MIN_WRITE_BEFORE_START_USECS = 200000; static const uint32_t AUDIOSTREAM_MIN_WRITE_BEFORE_START_USECS = 200000;
// The amount of instability we tollerate in calls to
// MediaDecoderStateMachine::UpdateEstimatedDuration(); changes of duration
// less than this are ignored, as they're assumed to be the result of
// instability in the duration estimation.
static const int64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2;
static TimeDuration UsecsToDuration(int64_t aUsecs) { static TimeDuration UsecsToDuration(int64_t aUsecs) {
return TimeDuration::FromMilliseconds(static_cast<double>(aUsecs) / USECS_PER_MS); return TimeDuration::FromMilliseconds(static_cast<double>(aUsecs) / USECS_PER_MS);
} }
@ -1444,9 +1450,12 @@ void MediaDecoderStateMachine::SetDuration(int64_t aDuration)
} }
} }
void MediaDecoderStateMachine::UpdateDuration(int64_t aDuration) void MediaDecoderStateMachine::UpdateEstimatedDuration(int64_t aDuration)
{ {
if (aDuration != GetDuration()) { mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
int64_t duration = GetDuration();
if (aDuration != duration &&
abs(aDuration - duration) > ESTIMATED_DURATION_FUZZ_FACTOR_USECS) {
SetDuration(aDuration); SetDuration(aDuration);
nsCOMPtr<nsIRunnable> event = nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(mDecoder, &MediaDecoder::DurationChanged); NS_NewRunnableMethod(mDecoder, &MediaDecoder::DurationChanged);

View File

@ -160,9 +160,12 @@ public:
// aEndTime is in microseconds. // aEndTime is in microseconds.
void SetMediaEndTime(int64_t aEndTime); void SetMediaEndTime(int64_t aEndTime);
// Called from decode thread to update the duration. Can result in // Called from main thread to update the duration with an estimated value.
// a durationchangeevent. aDuration is in microseconds. // The duration is only changed if its significantly different than the
void UpdateDuration(int64_t aDuration); // the current duration, as the incoming duration is an estimate and so
// often is unstable as more data is read and the estimate is updated.
// Can result in a durationchangeevent. aDuration is in microseconds.
void UpdateEstimatedDuration(int64_t aDuration);
// Functions used by assertions to ensure we're calling things // Functions used by assertions to ensure we're calling things
// on the appropriate threads. // on the appropriate threads.

View File

@ -36,12 +36,14 @@ GetDirectShowLog() {
DirectShowReader::DirectShowReader(AbstractMediaDecoder* aDecoder) DirectShowReader::DirectShowReader(AbstractMediaDecoder* aDecoder)
: MediaDecoderReader(aDecoder), : MediaDecoderReader(aDecoder),
mMP3FrameParser(aDecoder->GetResource()->GetLength()),
#ifdef DEBUG #ifdef DEBUG
mRotRegister(0), mRotRegister(0),
#endif #endif
mNumChannels(0), mNumChannels(0),
mAudioRate(0), mAudioRate(0),
mBytesPerSample(0) mBytesPerSample(0),
mDuration(0)
{ {
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread."); MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
MOZ_COUNT_CTOR(DirectShowReader); MOZ_COUNT_CTOR(DirectShowReader);
@ -366,4 +368,21 @@ DirectShowReader::OnDecodeThreadFinish()
CoUninitialize(); CoUninitialize();
} }
void
DirectShowReader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mMP3FrameParser.IsMP3()) {
return;
}
mMP3FrameParser.Parse(aBuffer, aLength, aOffset);
int64_t duration = mMP3FrameParser.GetDuration();
if (duration != mDuration) {
mDuration = duration;
MOZ_ASSERT(mDecoder);
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDecoder->UpdateEstimatedMediaDuration(mDuration);
}
}
} // namespace mozilla } // namespace mozilla

View File

@ -10,6 +10,7 @@
#include "Windows.h" // HRESULT, DWORD #include "Windows.h" // HRESULT, DWORD
#include "MediaDecoderReader.h" #include "MediaDecoderReader.h"
#include "mozilla/RefPtr.h" #include "mozilla/RefPtr.h"
#include "MP3FrameParser.h"
class IGraphBuilder; class IGraphBuilder;
class IMediaControl; class IMediaControl;
@ -70,6 +71,10 @@ public:
void OnDecodeThreadStart() MOZ_OVERRIDE; void OnDecodeThreadStart() MOZ_OVERRIDE;
void OnDecodeThreadFinish() MOZ_OVERRIDE; void OnDecodeThreadFinish() MOZ_OVERRIDE;
void NotifyDataArrived(const char* aBuffer,
uint32_t aLength,
int64_t aOffset) MOZ_OVERRIDE;
private: private:
// Calls mAudioQueue.Finish(), and notifies the filter graph that playback // Calls mAudioQueue.Finish(), and notifies the filter graph that playback
@ -91,6 +96,11 @@ private:
// The graph will block while this is blocked, i.e. it will pause decoding. // The graph will block while this is blocked, i.e. it will pause decoding.
RefPtr<AudioSinkFilter> mAudioSinkFilter; RefPtr<AudioSinkFilter> mAudioSinkFilter;
// Some MP3s are variable bitrate, so DirectShow's duration estimation
// can make its duration estimation based on the wrong bitrate. So we parse
// the MP3 frames to get a more accuate estimate of the duration.
MP3FrameParser mMP3FrameParser;
#ifdef DEBUG #ifdef DEBUG
// Used to add/remove the filter graph to the Running Object Table. You can // Used to add/remove the filter graph to the Running Object Table. You can
// connect GraphEdit/GraphStudio to the graph to observe and/or debug its // connect GraphEdit/GraphStudio to the graph to observe and/or debug its
@ -106,6 +116,9 @@ private:
// Number of bytes per sample. Can be either 1 or 2. // Number of bytes per sample. Can be either 1 or 2.
uint32_t mBytesPerSample; uint32_t mBytesPerSample;
// Duration of the stream, in microseconds.
int64_t mDuration;
}; };
} // namespace mozilla } // namespace mozilla

View File

@ -632,7 +632,7 @@ void OmxDecoder::NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_
MOZ_ASSERT(mDecoder); MOZ_ASSERT(mDecoder);
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mDecoder->UpdateMediaDuration(mDurationUs); mDecoder->UpdateEstimatedMediaDuration(mDurationUs);
} }
} }