bool WebMReader::IsDataCachedAtEndOfSubsegments() { MediaResource* resource = mDecoder->GetResource(); NS_ENSURE_TRUE(resource, false); if (resource->IsDataCachedToEndOfResource(0)) { return true; } if (mClusterByteRanges.IsEmpty()) { return false; } nsTArray<MediaByteRange> ranges; nsresult rv = resource->GetCachedRanges(ranges); NS_ENSURE_SUCCESS(rv, false); if (ranges.IsEmpty()) { return false; } // Return true if data at the end of the final subsegment is cached. uint32_t finalSubsegmentIndex = mClusterByteRanges.Length()-1; uint64_t finalSubEndOffset = mClusterByteRanges[finalSubsegmentIndex].mEnd; uint32_t finalRangeIndex = ranges.Length()-1; uint64_t finalRangeStartOffset = ranges[finalRangeIndex].mStart; uint64_t finalRangeEndOffset = ranges[finalRangeIndex].mEnd; return (finalRangeStartOffset < finalSubEndOffset && finalSubEndOffset <= finalRangeEndOffset); }
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 nsWebMReader::GetBuffered(nsTimeRanges* aBuffered, PRInt64 aStartTime) { MediaResource* resource = mDecoder->GetResource(); uint64_t timecodeScale; if (!mContext || nestegg_tstamp_scale(mContext, &timecodeScale) == -1) { return NS_OK; } // Special case completely cached files. This also handles local files. if (resource->IsDataCachedToEndOfResource(0)) { uint64_t duration = 0; if (nestegg_duration(mContext, &duration) == 0) { aBuffered->Add(0, duration / NS_PER_S); } } else { MediaResource* resource = mDecoder->GetResource(); nsTArray<MediaByteRange> ranges; nsresult res = resource->GetCachedRanges(ranges); NS_ENSURE_SUCCESS(res, res); PRInt64 startTimeOffsetNS = aStartTime * NS_PER_USEC; for (PRUint32 index = 0; index < ranges.Length(); index++) { mBufferedState->CalculateBufferedForRange(aBuffered, ranges[index].mStart, ranges[index].mEnd, timecodeScale, startTimeOffsetNS); } } return NS_OK; }
nsresult WebMReader::GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime) { MediaResource* resource = mDecoder->GetResource(); uint64_t timecodeScale; if (!mContext || nestegg_tstamp_scale(mContext, &timecodeScale) == -1) { return NS_OK; } // Special case completely cached files. This also handles local files. bool isFullyCached = resource->IsDataCachedToEndOfResource(0); if (isFullyCached) { uint64_t duration = 0; if (nestegg_duration(mContext, &duration) == 0) { aBuffered->Add(0, duration / NS_PER_S); } } uint32_t bufferedLength = 0; aBuffered->GetLength(&bufferedLength); // Either we the file is not fully cached, or we couldn't find a duration in // the WebM bitstream. if (!isFullyCached || !bufferedLength) { MediaResource* resource = mDecoder->GetResource(); nsTArray<MediaByteRange> ranges; nsresult res = resource->GetCachedRanges(ranges); NS_ENSURE_SUCCESS(res, res); for (uint32_t index = 0; index < ranges.Length(); index++) { uint64_t start, end; bool rv = mBufferedState->CalculateBufferedForRange(ranges[index].mStart, ranges[index].mEnd, &start, &end); if (rv) { double startTime = start * timecodeScale / NS_PER_S - aStartTime; double endTime = end * timecodeScale / NS_PER_S - aStartTime; // If this range extends to the end of the file, the true end time // is the file's duration. if (resource->IsDataCachedToEndOfResource(ranges[index].mStart)) { uint64_t duration = 0; if (nestegg_duration(mContext, &duration) == 0) { endTime = duration / NS_PER_S; } } aBuffered->Add(startTime, endTime); } } } return NS_OK; }
nsresult nsGStreamerReader::GetBuffered(nsTimeRanges* aBuffered, int64_t aStartTime) { if (!mInfo.mHasVideo && !mInfo.mHasAudio) { return NS_OK; } GstFormat format = GST_FORMAT_TIME; MediaResource* resource = mDecoder->GetResource(); gint64 resourceLength = resource->GetLength(); 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; GstFormat format = GST_FORMAT_TIME; duration = QueryDuration(); double end = (double) duration / GST_MSECOND; LOG(PR_LOG_DEBUG, ("complete range [0, %f] for [0, %li]", end, resourceLength)); 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 = 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, resourceLength)); aBuffered->Add(start, end); } return NS_OK; }
nsresult WebMReader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) { MediaResource* resource = mDecoder->GetResource(); uint64_t timecodeScale; if (!mContext || nestegg_tstamp_scale(mContext, &timecodeScale) == -1) { return NS_OK; } // Special case completely cached files. This also handles local files. bool isFullyCached = resource->IsDataCachedToEndOfResource(0); if (isFullyCached) { uint64_t duration = 0; if (nestegg_duration(mContext, &duration) == 0) { aBuffered->Add(0, duration / NS_PER_S); } } uint32_t bufferedLength = 0; aBuffered->GetLength(&bufferedLength); // Either we the file is not fully cached, or we couldn't find a duration in // the WebM bitstream. if (!isFullyCached || !bufferedLength) { MediaResource* resource = mDecoder->GetResource(); nsTArray<MediaByteRange> ranges; nsresult res = resource->GetCachedRanges(ranges); NS_ENSURE_SUCCESS(res, res); for (uint32_t index = 0; index < ranges.Length(); index++) { uint64_t start, end; bool rv = mBufferedState->CalculateBufferedForRange(ranges[index].mStart, ranges[index].mEnd, &start, &end); if (rv) { double startTime = start * timecodeScale / NS_PER_S - aStartTime; double endTime = end * timecodeScale / NS_PER_S - aStartTime; #ifdef MOZ_DASH // If this range extends to the end of a cluster, the true end time is // the cluster's end timestamp. Since WebM frames do not have an end // timestamp, a fully cached cluster must be reported with the correct // end time of its final frame. Otherwise, buffered ranges could be // reported with missing frames at cluster boundaries, specifically // boundaries where stream switching has occurred. if (!mClusterByteRanges.IsEmpty()) { for (uint32_t clusterIndex = 0; clusterIndex < (mClusterByteRanges.Length()-1); clusterIndex++) { if (ranges[index].mEnd >= mClusterByteRanges[clusterIndex].mEnd) { double clusterEndTime = mClusterByteRanges[clusterIndex+1].mStartTime / USEC_PER_S; if (endTime < clusterEndTime) { LOG(PR_LOG_DEBUG, ("End of cluster: endTime becoming %0.3fs", clusterEndTime)); endTime = clusterEndTime; } } } } #endif // If this range extends to the end of the file, the true end time // is the file's duration. if (resource->IsDataCachedToEndOfResource(ranges[index].mStart)) { uint64_t duration = 0; if (nestegg_duration(mContext, &duration) == 0) { endTime = duration / NS_PER_S; } } aBuffered->Add(startTime, endTime); } } } return NS_OK; }