--- a/content/media/gstreamer/GStreamerReader.cpp
+++ b/content/media/gstreamer/GStreamerReader.cpp
@@ -63,16 +63,17 @@ typedef enum {
GST_PLAY_FLAG_BUFFERING = (1 << 8),
GST_PLAY_FLAG_DEINTERLACE = (1 << 9),
GST_PLAY_FLAG_SOFT_COLORBALANCE = (1 << 10)
} PlayFlags;
GStreamerReader::GStreamerReader(AbstractMediaDecoder* aDecoder)
: MediaDecoderReader(aDecoder),
mMP3FrameParser(aDecoder->GetResource()->GetLength()),
+ mDataOffset(0),
mUseParserDuration(false),
#if GST_VERSION_MAJOR >= 1
mAllocator(nullptr),
mBufferPool(nullptr),
#endif
mPlayBin(nullptr),
mBus(nullptr),
mSource(nullptr),
@@ -233,17 +234,17 @@ void GStreamerReader::PlayBinSourceSetup
* returns something meaningful and not -1
*/
char buf[512];
unsigned int size = 0;
resource->Read(buf, sizeof(buf), &size);
resource->Seek(SEEK_SET, 0);
/* now we should have a length */
- int64_t resourceLength = resource->GetLength();
+ int64_t resourceLength = GetDataLength();
gst_app_src_set_size(mSource, resourceLength);
if (resource->IsDataCachedToEndOfResource(0) ||
(resourceLength != -1 && resourceLength <= SHORT_FILE_SIZE)) {
/* let the demuxer work in pull mode for local files (or very short files)
* so that we get optimal seeking accuracy/performance
*/
LOG(PR_LOG_DEBUG, "configuring random access, len %lld", resourceLength);
gst_app_src_set_stream_type(mSource, GST_APP_STREAM_TYPE_RANDOM_ACCESS);
@@ -282,21 +283,38 @@ nsresult GStreamerReader::ParseMP3Header
NS_ENSURE_TRUE(bytesRead, NS_ERROR_FAILURE);
mMP3FrameParser.Parse(bytes, bytesRead, offset);
offset += bytesRead;
} while (!mMP3FrameParser.ParsedHeaders());
if (mMP3FrameParser.IsMP3()) {
mLastParserDuration = mMP3FrameParser.GetDuration();
+ mDataOffset = mMP3FrameParser.GetMP3Offset();
+
+ // Update GStreamer's stream length in case we found any ID3 headers to
+ // ignore.
+ gst_app_src_set_size(mSource, GetDataLength());
}
return NS_OK;
}
+int64_t
+GStreamerReader::GetDataLength()
+{
+ int64_t streamLen = mDecoder->GetResource()->GetLength();
+
+ if (streamLen < 0) {
+ return streamLen;
+ }
+
+ return streamLen - mDataOffset;
+}
+
nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags)
{
NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
nsresult ret = NS_OK;
/*
* Parse MP3 headers before we kick off the GStreamer pipeline otherwise there
@@ -792,17 +810,17 @@ nsresult GStreamerReader::GetBuffered(do
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
duration = mDecoder->GetMediaDuration();
}
double end = (double) duration / GST_MSECOND;
LOG(PR_LOG_DEBUG, "complete range [0, %f] for [0, %li]",
- end, resource->GetLength());
+ end, GetDataLength());
aBuffered->Add(0, end);
return NS_OK;
}
for(uint32_t index = 0; index < ranges.Length(); index++) {
int64_t startOffset = ranges[index].mStart;
int64_t endOffset = ranges[index].mEnd;
gint64 startTime, endTime;
@@ -821,17 +839,17 @@ nsresult GStreamerReader::GetBuffered(do
if (!gst_element_query_convert(GST_ELEMENT(mPlayBin), GST_FORMAT_BYTES,
endOffset, &format, &endTime) || format != GST_FORMAT_TIME)
continue;
#endif
double start = (double) GST_TIME_AS_USECONDS (startTime) / GST_MSECOND;
double end = (double) GST_TIME_AS_USECONDS (endTime) / GST_MSECOND;
LOG(PR_LOG_DEBUG, "adding range [%f, %f] for [%li %li] size %li",
- start, end, startOffset, endOffset, resource->GetLength());
+ start, end, startOffset, endOffset, GetDataLength());
aBuffered->Add(start, end);
}
return NS_OK;
}
void GStreamerReader::ReadAndPushData(guint aLength)
{
@@ -925,25 +943,27 @@ gboolean GStreamerReader::SeekDataCb(Gst
gpointer aUserData)
{
GStreamerReader* reader = reinterpret_cast<GStreamerReader*>(aUserData);
return reader->SeekData(aSrc, aOffset);
}
gboolean GStreamerReader::SeekData(GstAppSrc* aSrc, guint64 aOffset)
{
+ aOffset += mDataOffset;
+
ReentrantMonitorAutoEnter mon(mGstThreadsMonitor);
MediaResource* resource = mDecoder->GetResource();
int64_t resourceLength = resource->GetLength();
if (gst_app_src_get_size(mSource) == -1) {
/* It's possible that we didn't know the length when we initialized mSource
* but maybe we do now
*/
- gst_app_src_set_size(mSource, resourceLength);
+ gst_app_src_set_size(mSource, GetDataLength());
}
nsresult rv = NS_ERROR_FAILURE;
if (aOffset < static_cast<guint64>(resourceLength)) {
rv = resource->Seek(SEEK_SET, aOffset);
}
if (NS_FAILED(rv)) {
--- a/content/media/gstreamer/GStreamerReader.h
+++ b/content/media/gstreamer/GStreamerReader.h
@@ -173,19 +173,27 @@ private:
*/
static GValueArray* AutoplugSortCb(GstElement* aElement,
GstPad* aPad, GstCaps* aCaps,
GValueArray* aFactories);
// Try to find MP3 headers in this stream using our MP3 frame parser.
nsresult ParseMP3Headers();
+ // Get the length of the stream, excluding any metadata we have ignored at the
+ // start of the stream: ID3 headers, for example.
+ int64_t GetDataLength();
+
// Use our own MP3 parser here, largely for consistency with other platforms.
MP3FrameParser mMP3FrameParser;
+ // The byte position in the stream where the actual media (ignoring, for
+ // example, ID3 tags) starts.
+ uint64_t mDataOffset;
+
// 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;