nsresult GStreamerReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) { if (!mInfo.HasValidMedia()) { return NS_OK; } #if GST_VERSION_MAJOR == 0 GstFormat format = GST_FORMAT_TIME; #endif MediaResource* resource = mDecoder->GetResource(); nsTArray<MediaByteRange> ranges; resource->GetCachedRanges(ranges); if (resource->IsDataCachedToEndOfResource(0)) { /* fast path for local or completely cached files */ gint64 duration = 0; { 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()); 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; #if GST_VERSION_MAJOR >= 1 if (!gst_element_query_convert(GST_ELEMENT(mPlayBin), GST_FORMAT_BYTES, startOffset, GST_FORMAT_TIME, &startTime)) continue; if (!gst_element_query_convert(GST_ELEMENT(mPlayBin), GST_FORMAT_BYTES, endOffset, GST_FORMAT_TIME, &endTime)) continue; #else if (!gst_element_query_convert(GST_ELEMENT(mPlayBin), GST_FORMAT_BYTES, startOffset, &format, &startTime) || format != GST_FORMAT_TIME) continue; 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()); aBuffered->Add(start, end); } return NS_OK; }
nsresult GStreamerReader::GetBuffered(TimeRanges* aBuffered, int64_t aStartTime) { if (!mInfo.mHasVideo && !mInfo.mHasAudio) { return NS_OK; } GstFormat format = GST_FORMAT_TIME; MediaResource* resource = mDecoder->GetResource(); nsTArray<MediaByteRange> ranges; resource->GetCachedRanges(ranges); if (mDecoder->OnStateMachineThread()) /* Report the position from here while buffering as we can't report it from * the gstreamer threads that are actually reading from the resource */ NotifyBytesConsumed(); if (resource->IsDataCachedToEndOfResource(0)) { /* fast path for local or completely cached files */ gint64 duration = 0; duration = QueryDuration(); double end = (double) duration / GST_MSECOND; LOG(PR_LOG_DEBUG, ("complete range [0, %f] for [0, %li]", end, resource->GetLength())); 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; if (!gst_element_query_convert(GST_ELEMENT(mPlayBin), GST_FORMAT_BYTES, startOffset, &format, &startTime) || format != GST_FORMAT_TIME) continue; if (!gst_element_query_convert(GST_ELEMENT(mPlayBin), GST_FORMAT_BYTES, endOffset, &format, &endTime) || format != GST_FORMAT_TIME) continue; 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())); aBuffered->Add(start, end); } return NS_OK; }
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, GetDataLength()); } nsresult rv = NS_ERROR_FAILURE; if (aOffset < static_cast<guint64>(resourceLength)) { rv = resource->Seek(SEEK_SET, aOffset); } if (NS_FAILED(rv)) { LOG(PR_LOG_ERROR, "seek at %lu failed", aOffset); } else { MOZ_ASSERT(aOffset == static_cast<guint64>(resource->Tell())); } return NS_SUCCEEDED(rv); }
void GStreamerReader::PlayBinSourceSetup(GstAppSrc* aSource) { mSource = GST_APP_SRC(aSource); gst_app_src_set_callbacks(mSource, &mSrcCallbacks, (gpointer) this, nullptr); MediaResource* resource = mDecoder->GetResource(); /* do a short read to trigger a network request so that GetLength() below * 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(); 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); } else { /* make the demuxer work in push mode so that seeking is kept to a minimum */ LOG(PR_LOG_DEBUG, ("configuring push mode, len %lld", resourceLength)); gst_app_src_set_stream_type(mSource, GST_APP_STREAM_TYPE_SEEKABLE); } }
gboolean nsGStreamerReader::SeekData(GstAppSrc *aSrc, guint64 aOffset) { ReentrantMonitorAutoEnter mon(mGstThreadsMonitor); MediaResource* resource = mDecoder->GetResource(); 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, resource->GetLength()); nsresult rv = NS_ERROR_FAILURE; if (aOffset < resource->GetLength()) rv = resource->Seek(SEEK_SET, aOffset); if (NS_SUCCEEDED(rv)) mByteOffset = mLastReportedByteOffset = aOffset; else LOG(PR_LOG_ERROR, ("seek at %lu failed", aOffset)); return NS_SUCCEEDED(rv); }
nsresult MediaOmxReader::GetBuffered(mozilla::dom::TimeRanges* aBuffered, int64_t aStartTime) { if (!mOmxDecoder.get()) return NS_OK; MediaResource* stream = mOmxDecoder->GetResource(); int64_t durationUs = 0; mOmxDecoder->GetDuration(&durationUs); // Nothing to cache if the media takes 0us to play. if (!durationUs) return NS_OK; // Special case completely cached files. This also handles local files. if (stream->IsDataCachedToEndOfResource(0)) { aBuffered->Add(0, durationUs); return NS_OK; } int64_t totalBytes = stream->GetLength(); // If we can't determine the total size, pretend that we have nothing // buffered. This will put us in a state of eternally-low-on-undecoded-data // which is not get, but about the best we can do. if (totalBytes == -1) return NS_OK; int64_t startOffset = stream->GetNextCachedData(0); while (startOffset >= 0) { int64_t endOffset = stream->GetCachedDataEnd(startOffset); // Bytes [startOffset..endOffset] are cached. NS_ASSERTION(startOffset >= 0, "Integer underflow in GetBuffered"); NS_ASSERTION(endOffset >= 0, "Integer underflow in GetBuffered"); uint64_t startUs = BytesToTime(startOffset, totalBytes, durationUs); uint64_t endUs = BytesToTime(endOffset, totalBytes, durationUs); if (startUs != endUs) { aBuffered->Add((double)startUs / USECS_PER_S, (double)endUs / USECS_PER_S); } startOffset = stream->GetNextCachedData(endOffset); } return NS_OK; }
void nsGStreamerReader::PlayBinSourceSetup(GstAppSrc *aSource) { mSource = GST_APP_SRC(aSource); gst_app_src_set_callbacks(mSource, &mSrcCallbacks, (gpointer) this, NULL); MediaResource* resource = mDecoder->GetResource(); int64_t len = resource->GetLength(); gst_app_src_set_size(mSource, len); if (resource->IsDataCachedToEndOfResource(0) || (len != -1 && len <= 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_ERROR, ("configuring random access")); gst_app_src_set_stream_type(mSource, GST_APP_STREAM_TYPE_RANDOM_ACCESS); } else { /* make the demuxer work in push mode so that seeking is kept to a minimum */ gst_app_src_set_stream_type(mSource, GST_APP_STREAM_TYPE_SEEKABLE); } }
nsresult RawReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) { NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); MediaResource* resource = mDecoder->GetResource(); NS_ASSERTION(resource, "Decoder has no media resource"); if (!ReadFromResource(resource, reinterpret_cast<uint8_t*>(&mMetadata), sizeof(mMetadata))) return NS_ERROR_FAILURE; // Validate the header if (!(mMetadata.headerPacketID == 0 /* Packet ID of 0 for the header*/ && mMetadata.codecID == RAW_ID /* "YUV" */ && mMetadata.majorVersion == 0 && mMetadata.minorVersion == 1)) return NS_ERROR_FAILURE; CheckedUint32 dummy = CheckedUint32(static_cast<uint32_t>(mMetadata.frameWidth)) * static_cast<uint32_t>(mMetadata.frameHeight); NS_ENSURE_TRUE(dummy.isValid(), NS_ERROR_FAILURE); if (mMetadata.aspectDenominator == 0 || mMetadata.framerateDenominator == 0) return NS_ERROR_FAILURE; // Invalid data // Determine and verify frame display size. float pixelAspectRatio = static_cast<float>(mMetadata.aspectNumerator) / mMetadata.aspectDenominator; nsIntSize display(mMetadata.frameWidth, mMetadata.frameHeight); ScaleDisplayByAspectRatio(display, pixelAspectRatio); mPicture = nsIntRect(0, 0, mMetadata.frameWidth, mMetadata.frameHeight); nsIntSize frameSize(mMetadata.frameWidth, mMetadata.frameHeight); if (!IsValidVideoRegion(frameSize, mPicture, display)) { // Video track's frame sizes will overflow. Fail. return NS_ERROR_FAILURE; } mInfo.mVideo.mDisplay = display; mFrameRate = static_cast<float>(mMetadata.framerateNumerator) / mMetadata.framerateDenominator; // Make some sanity checks if (mFrameRate > 45 || mFrameRate == 0 || pixelAspectRatio == 0 || mMetadata.frameWidth > 2000 || mMetadata.frameHeight > 2000 || mMetadata.chromaChannelBpp != 4 || mMetadata.lumaChannelBpp != 8 || mMetadata.colorspace != 1 /* 4:2:0 */) return NS_ERROR_FAILURE; mFrameSize = mMetadata.frameWidth * mMetadata.frameHeight * (mMetadata.lumaChannelBpp + mMetadata.chromaChannelBpp) / 8.0 + sizeof(RawPacketHeader); int64_t length = resource->GetLength(); if (length != -1) { ReentrantMonitorAutoEnter autoMonitor(mDecoder->GetReentrantMonitor()); mDecoder->SetMediaDuration(USECS_PER_S * (length - sizeof(RawVideoHeader)) / (mFrameSize * mFrameRate)); } *aInfo = mInfo; *aTags = nullptr; return NS_OK; }