Bug 918135 - Use MP3FrameParser in GStreamer backend for MP3 duration estimation r=cpearce

This patch adds support for our home-grown MP3 frame parser to the GStreamer
backend so that our duration estimation is consistent across all platforms.
This commit is contained in:
Edwin Flores 2013-12-03 10:25:28 +13:00
parent 39b4d9dbec
commit 740ce45445
2 changed files with 99 additions and 2 deletions

View File

@ -5,6 +5,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsError.h"
#include "nsMimeTypes.h"
#include "MediaDecoderStateMachine.h"
#include "AbstractMediaDecoder.h"
#include "MediaResource.h"
@ -57,6 +58,8 @@ typedef enum {
GStreamerReader::GStreamerReader(AbstractMediaDecoder* aDecoder)
: MediaDecoderReader(aDecoder),
mMP3FrameParser(aDecoder->GetResource()->GetLength()),
mUseParserDuration(false),
mPlayBin(nullptr),
mBus(nullptr),
mSource(nullptr),
@ -243,6 +246,35 @@ void GStreamerReader::PlayBinSourceSetup(GstAppSrc* aSource)
gst_caps_unref(caps);
}
/**
* If this stream is an MP3, we want to parse the headers to estimate the
* stream duration.
*/
nsresult GStreamerReader::ParseMP3Headers()
{
MediaResource *resource = mDecoder->GetResource();
const uint32_t MAX_READ_BYTES = 4096;
uint64_t offset = 0;
char bytes[MAX_READ_BYTES];
uint32_t bytesRead;
do {
nsresult rv = resource->ReadAt(offset, bytes, MAX_READ_BYTES, &bytesRead);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(bytesRead, NS_ERROR_FAILURE);
mMP3FrameParser.Parse(bytes, bytesRead, offset);
offset += bytesRead;
} while (!mMP3FrameParser.ParsedHeaders());
if (mMP3FrameParser.IsMP3()) {
mLastParserDuration = mMP3FrameParser.GetDuration();
}
return NS_OK;
}
nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags)
{
@ -338,16 +370,32 @@ nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo,
}
}
bool isMP3 = mDecoder->GetResource()->GetContentType().EqualsASCII(AUDIO_MP3);
if (isMP3) {
ParseMP3Headers();
}
/* report the duration */
gint64 duration;
GstFormat format = GST_FORMAT_TIME;
if (gst_element_query_duration(GST_ELEMENT(mPlayBin),
if (isMP3 && mMP3FrameParser.IsMP3()) {
// The MP3FrameParser has reported a duration; use that over the gstreamer
// reported duration for inter-platform consistency.
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mUseParserDuration = true;
mLastParserDuration = mMP3FrameParser.GetDuration();
mDecoder->SetMediaDuration(mLastParserDuration);
} else if (gst_element_query_duration(GST_ELEMENT(mPlayBin),
&format, &duration) && format == GST_FORMAT_TIME) {
// Otherwise use the gstreamer duration.
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
LOG(PR_LOG_DEBUG, ("returning duration %" GST_TIME_FORMAT,
GST_TIME_ARGS (duration)));
duration = GST_TIME_AS_USECONDS (duration);
mDecoder->SetMediaDuration(duration);
} else {
mDecoder->SetMediaSeekable(false);
}
@ -1019,5 +1067,33 @@ void GStreamerReader::Eos()
}
}
/**
* If this is an MP3 stream, pass any new data we get to the MP3 frame parser
* for duration estimation.
*/
void GStreamerReader::NotifyDataArrived(const char *aBuffer,
uint32_t aLength,
int64_t aOffset)
{
MOZ_ASSERT(NS_IsMainThread());
if (HasVideo()) {
return;
}
if (!mMP3FrameParser.NeedsData()) {
return;
}
mMP3FrameParser.Parse(aBuffer, aLength, aOffset);
int64_t duration = mMP3FrameParser.GetDuration();
if (duration != mLastParserDuration && mUseParserDuration) {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mLastParserDuration = duration;
mDecoder->UpdateEstimatedMediaDuration(mLastParserDuration);
}
}
} // namespace mozilla

View File

@ -5,6 +5,8 @@
#if !defined(GStreamerReader_h_)
#define GStreamerReader_h_
#include <map>
#include <gst/gst.h>
#include <gst/app/gstappsrc.h>
#include <gst/app/gstappsink.h>
@ -17,8 +19,9 @@
#pragma GCC diagnostic ignored "-Wreserved-user-defined-literal"
#include <gst/video/video.h>
#pragma GCC diagnostic pop
#include <map>
#include "MediaDecoderReader.h"
#include "MP3FrameParser.h"
#include "nsRect.h"
namespace mozilla {
@ -52,6 +55,10 @@ public:
int64_t aCurrentTime);
virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime);
virtual void NotifyDataArrived(const char *aBuffer,
uint32_t aLength,
int64_t aOffset) MOZ_OVERRIDE;
virtual bool HasAudio() {
return mInfo.HasAudio();
}
@ -129,6 +136,20 @@ private:
static void EosCb(GstAppSink* aSink, gpointer aUserData);
void Eos();
// Try to find MP3 headers in this stream using our MP3 frame parser.
nsresult ParseMP3Headers();
// Use our own MP3 parser here, largely for consistency with other platforms.
MP3FrameParser mMP3FrameParser;
// We want to be able to decide in |ReadMetadata| whether or not we use the
// duration from the MP3 frame parser, as this backend supports more than just
// MP3. But |NotifyDataArrived| can update the duration and is often called
// _before_ |ReadMetadata|. This flag stops the former from using the parser
// duration until we are sure we want to.
bool mUseParserDuration;
int64_t mLastParserDuration;
GstElement* mPlayBin;
GstBus* mBus;
GstAppSrc* mSource;