Exemple #1
0
nsresult WebMReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndTime,
                          int64_t aCurrentTime)
{
    NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");

    LOG(PR_LOG_DEBUG, ("Reader [%p] for Decoder [%p]: About to seek to %fs",
                       this, mDecoder, aTarget/1000000.0));
    if (NS_FAILED(ResetDecode())) {
        return NS_ERROR_FAILURE;
    }
    uint32_t trackToSeek = mHasVideo ? mVideoTrack : mAudioTrack;
    int r = nestegg_track_seek(mContext, trackToSeek, aTarget * NS_PER_USEC);
    if (r != 0) {
        return NS_ERROR_FAILURE;
    }
#ifdef MOZ_DASH
    // Find next cluster index;
    MediaResource* resource = mDecoder->GetResource();
    int64_t newOffset = resource->Tell();
    for (uint32_t i = 1; i < mClusterByteRanges.Length(); i++) {
        if (newOffset < mClusterByteRanges[i].mStart) {
            mNextCluster = i;
            LOG(PR_LOG_DEBUG,
                ("WebMReader [%p] for decoder [%p] updating mNextCluster to [%d] "
                 "after seek to offset [%lld]",
                 this, mDecoder, mNextCluster, resource->Tell()));
            break;
        }
    }
#endif
    return DecodeToTarget(aTarget);
}
/*
 * If we're not at end of stream, read |aNumBytes| from the media resource,
 * put it in |aData|, and return true.
 * Otherwise, put as much data as is left into |aData|, set |aNumBytes| to the
 * amount of data we have left, and return false.
 */
nsresult
AppleMP3Reader::Read(uint32_t *aNumBytes, char *aData)
{
  MediaResource *resource = mDecoder->GetResource();

  // Loop until we have all the data asked for, or we've reached EOS
  uint32_t totalBytes = 0;
  uint32_t numBytes;
  do {
    uint32_t bytesWanted = *aNumBytes - totalBytes;
    nsresult rv = resource->Read(aData + totalBytes, bytesWanted, &numBytes);
    totalBytes += numBytes;

    if (NS_FAILED(rv)) {
      *aNumBytes = 0;
      return NS_ERROR_FAILURE;
    }
  } while(totalBytes < *aNumBytes && numBytes);

  *aNumBytes = totalBytes;

  // We will have read some data in the last iteration iff we filled the buffer.
  // XXX Maybe return a better value than NS_ERROR_FAILURE?
  return numBytes ? NS_OK : NS_ERROR_FAILURE;
}
Exemple #3
0
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);
}
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);
  }
}
Exemple #5
0
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;
}
Exemple #6
0
/**
 * 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();
    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;
}
void nsGStreamerReader::ReadAndPushData(guint aLength)
{
  MediaResource* resource = mDecoder->GetResource();
  NS_ASSERTION(resource, "Decoder has no media resource");
  nsresult rv = NS_OK;

  GstBuffer *buffer = gst_buffer_new_and_alloc(aLength);
  guint8 *data = GST_BUFFER_DATA(buffer);
  uint32_t size = 0, bytesRead = 0;
  while(bytesRead < aLength) {
    rv = resource->Read(reinterpret_cast<char*>(data + bytesRead),
        aLength - bytesRead, &size);
    if (NS_FAILED(rv) || size == 0)
      break;

    bytesRead += size;
  }

  GST_BUFFER_SIZE(buffer) = bytesRead;
  mByteOffset += bytesRead;

  GstFlowReturn ret = gst_app_src_push_buffer(mSource, gst_buffer_ref(buffer));
  if (ret != GST_FLOW_OK)
    LOG(PR_LOG_ERROR, ("ReadAndPushData push ret %s", gst_flow_get_name(ret)));

  if (GST_BUFFER_SIZE (buffer) < aLength)
    /* If we read less than what we wanted, we reached the end */
    gst_app_src_end_of_stream(mSource);

  gst_buffer_unref(buffer);
}
// Functions for reading and seeking using MediaResource required for
// nestegg_io. The 'user data' passed to these functions is the
// decoder from which the media resource is obtained.
static int webm_read(void *aBuffer, size_t aLength, void *aUserData)
{
  NS_ASSERTION(aUserData, "aUserData must point to a valid nsBuiltinDecoder");
  nsBuiltinDecoder* decoder = reinterpret_cast<nsBuiltinDecoder*>(aUserData);
  MediaResource* resource = decoder->GetResource();
  NS_ASSERTION(resource, "Decoder has no media resource");

  nsresult rv = NS_OK;
  bool eof = false;

  char *p = static_cast<char *>(aBuffer);
  while (NS_SUCCEEDED(rv) && aLength > 0) {
    PRUint32 bytes = 0;
    rv = resource->Read(p, aLength, &bytes);
    if (bytes == 0) {
      eof = true;
      break;
    }
    decoder->NotifyBytesConsumed(bytes);
    aLength -= bytes;
    p += bytes;
  }

  return NS_FAILED(rv) ? -1 : eof ? 0 : 1;
}
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;
}
Exemple #10
0
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);
}
Exemple #11
0
static int64_t webm_tell(void *aUserData)
{
  NS_ASSERTION(aUserData, "aUserData must point to a valid AbstractMediaDecoder");
  AbstractMediaDecoder* decoder = reinterpret_cast<AbstractMediaDecoder*>(aUserData);
  MediaResource* resource = decoder->GetResource();
  NS_ASSERTION(resource, "Decoder has no media resource");
  return resource->Tell();
}
Exemple #12
0
static int webm_seek(int64_t aOffset, int aWhence, void *aUserData)
{
  NS_ASSERTION(aUserData, "aUserData must point to a valid AbstractMediaDecoder");
  AbstractMediaDecoder* decoder = reinterpret_cast<AbstractMediaDecoder*>(aUserData);
  MediaResource* resource = decoder->GetResource();
  NS_ASSERTION(resource, "Decoder has no media resource");
  nsresult rv = resource->Seek(aWhence, aOffset);
  return NS_SUCCEEDED(rv) ? 0 : -1;
}
void MediaDecoder::PinForSeek()
{
  MediaResource* resource = GetResource();
  if (!resource || mPinnedForSeek) {
    return;
  }
  mPinnedForSeek = true;
  resource->Pin();
}
void MediaDecoder::UnpinForSeek()
{
  MediaResource* resource = GetResource();
  if (!resource || !mPinnedForSeek) {
    return;
  }
  mPinnedForSeek = false;
  resource->Unpin();
}
Exemple #15
0
void MediaDecoder::UnpinForSeek()
{
  MOZ_ASSERT(NS_IsMainThread());
  MediaResource* resource = GetResource();
  if (!resource || !mPinnedForSeek) {
    return;
  }
  mPinnedForSeek = false;
  resource->Unpin();
}
Exemple #16
0
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;
}
Exemple #18
0
nsresult nsRawReader::Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime)
{
  NS_ASSERTION(mDecoder->OnDecodeThread(),
               "Should be on decode thread.");

  MediaResource *resource = mDecoder->GetResource();
  NS_ASSERTION(resource, "Decoder has no media resource");

  uint32_t frame = mCurrentFrame;
  if (aTime >= UINT_MAX)
    return NS_ERROR_FAILURE;
  mCurrentFrame = aTime * mFrameRate / USECS_PER_S;

  CheckedUint32 offset = CheckedUint32(mCurrentFrame) * mFrameSize;
  offset += sizeof(nsRawVideoHeader);
  NS_ENSURE_TRUE(offset.isValid(), NS_ERROR_FAILURE);

  nsresult rv = resource->Seek(nsISeekableStream::NS_SEEK_SET, offset.value());
  NS_ENSURE_SUCCESS(rv, rv);

  mVideoQueue.Erase();

  while(mVideoQueue.GetSize() == 0) {
    bool keyframeSkip = false;
    if (!DecodeVideoFrame(keyframeSkip, 0)) {
      mCurrentFrame = frame;
      return NS_ERROR_FAILURE;
    }

    {
      mozilla::ReentrantMonitorAutoEnter autoMonitor(mDecoder->GetReentrantMonitor());
      if (mDecoder->GetDecodeState() ==
          nsBuiltinDecoderStateMachine::DECODER_STATE_SHUTDOWN) {
        mCurrentFrame = frame;
        return NS_ERROR_FAILURE;
      }
    }

    nsAutoPtr<VideoData> video(mVideoQueue.PeekFront());
    if (video && video->mEndTime < aTime) {
      mVideoQueue.PopFront();
      video = nullptr;
    } else {
      video.forget();
    }
  }

  return NS_OK;
}
static bool Read(Decoder *aDecoder, char *aBuffer, int64_t aOffset, uint32_t aCount, uint32_t* aBytes)
{
  MediaResource *resource = GetResource(aDecoder);
  if (aOffset != resource->Tell()) {
    nsresult rv = resource->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
    if (NS_FAILED(rv)) {
      return false;
    }
  }
  nsresult rv = resource->Read(aBuffer, aCount, aBytes);
  if (NS_FAILED(rv)) {
    return false;
  }
  return true;
}
Exemple #20
0
nsresult RawReader::SeekInternal(int64_t aTime)
{
  NS_ASSERTION(mDecoder->OnDecodeThread(),
               "Should be on decode thread.");

  MediaResource *resource = mDecoder->GetResource();
  NS_ASSERTION(resource, "Decoder has no media resource");

  uint32_t frame = mCurrentFrame;
  if (aTime >= UINT_MAX)
    return NS_ERROR_FAILURE;
  mCurrentFrame = aTime * mFrameRate / USECS_PER_S;

  CheckedUint32 offset = CheckedUint32(mCurrentFrame) * mFrameSize;
  offset += sizeof(RawVideoHeader);
  NS_ENSURE_TRUE(offset.isValid(), NS_ERROR_FAILURE);

  nsresult rv = resource->Seek(nsISeekableStream::NS_SEEK_SET, offset.value());
  NS_ENSURE_SUCCESS(rv, rv);

  mVideoQueue.Reset();

  while(mVideoQueue.GetSize() == 0) {
    bool keyframeSkip = false;
    if (!DecodeVideoFrame(keyframeSkip, 0)) {
      mCurrentFrame = frame;
      return NS_ERROR_FAILURE;
    }

    {
      ReentrantMonitorAutoEnter autoMonitor(mDecoder->GetReentrantMonitor());
      if (mDecoder->IsShutdown()) {
        mCurrentFrame = frame;
        return NS_ERROR_FAILURE;
      }
    }

    if (mVideoQueue.PeekFront() && mVideoQueue.PeekFront()->GetEndTime() < aTime) {
      nsRefPtr<VideoData> releaseMe = mVideoQueue.PopFront();
    }
  }

  return NS_OK;
}
Exemple #21
0
void GStreamerReader::ReadAndPushData(guint aLength)
{
  MediaResource* resource = mDecoder->GetResource();
  NS_ASSERTION(resource, "Decoder has no media resource");
  nsresult rv = NS_OK;

  GstBuffer* buffer = gst_buffer_new_and_alloc(aLength);
#if GST_VERSION_MAJOR >= 1
  GstMapInfo info;
  gst_buffer_map(buffer, &info, GST_MAP_WRITE);
  guint8 *data = info.data;
#else
  guint8* data = GST_BUFFER_DATA(buffer);
#endif
  uint32_t size = 0, bytesRead = 0;
  while(bytesRead < aLength) {
    rv = resource->Read(reinterpret_cast<char*>(data + bytesRead),
        aLength - bytesRead, &size);
    if (NS_FAILED(rv) || size == 0)
      break;

    bytesRead += size;
  }

#if GST_VERSION_MAJOR >= 1
  gst_buffer_unmap(buffer, &info);
  gst_buffer_set_size(buffer, bytesRead);
#else
  GST_BUFFER_SIZE(buffer) = bytesRead;
#endif

  GstFlowReturn ret = gst_app_src_push_buffer(mSource, gst_buffer_ref(buffer));
  if (ret != GST_FLOW_OK) {
    LOG(PR_LOG_ERROR, ("ReadAndPushData push ret %s", gst_flow_get_name(ret)));
  }

  if (bytesRead < aLength) {
    /* If we read less than what we wanted, we reached the end */
    gst_app_src_end_of_stream(mSource);
  }

  gst_buffer_unref(buffer);
}
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);
  }
}
/*
 * If we're not at end of stream, read |aNumBytes| from the media resource,
 * put it in |aData|, and return true.
 * Otherwise, put as much data as is left into |aData|, set |aNumBytes| to the
 * amount of data we have left, and return false.
 *
 * This function also passes the read data on to the MP3 frame parser for
 * stream duration estimation.
 */
nsresult
AppleMP3Reader::ReadAndNotify(uint32_t *aNumBytes, char *aData)
{
    MediaResource *resource = mDecoder->GetResource();

    uint64_t offset = resource->Tell();

    // Loop until we have all the data asked for, or we've reached EOS
    uint32_t totalBytes = 0;
    uint32_t numBytes;
    do {
        uint32_t bytesWanted = *aNumBytes - totalBytes;
        nsresult rv = resource->Read(aData + totalBytes, bytesWanted, &numBytes);
        totalBytes += numBytes;

        if (NS_FAILED(rv)) {
            *aNumBytes = 0;
            return NS_ERROR_FAILURE;
        }
    } while(totalBytes < *aNumBytes && numBytes);

    // Pass the buffer to the MP3 frame parser to improve our duration estimate.
    if (mMP3FrameParser.IsMP3()) {
        mMP3FrameParser.Parse(aData, totalBytes, offset);
        uint64_t duration = mMP3FrameParser.GetDuration();
        if (duration != mDuration) {
            LOGD("Updating media duration to %lluus\n", duration);
            mDuration = duration;
            ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
            mDecoder->UpdateEstimatedMediaDuration(duration);
        }
    }

    *aNumBytes = totalBytes;

    // We will have read some data in the last iteration iff we filled the buffer.
    // XXX Maybe return a better value than NS_ERROR_FAILURE?
    return numBytes ? NS_OK : NS_ERROR_FAILURE;
}
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);
}
Exemple #25
0
/**
 * 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;
}
Exemple #26
0
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;
}
nsresult
nsDASHReader::GetBuffered(nsTimeRanges* aBuffered,
                          int64_t aStartTime)
{
  NS_ENSURE_ARG(aBuffered);

  MediaResource* resource = nullptr;
  nsBuiltinDecoder* decoder = nullptr;

  // Need to find intersect of |nsTimeRanges| for audio and video.
  nsTimeRanges audioBuffered, videoBuffered;
  uint32_t audioRangeCount, videoRangeCount;

  nsresult rv = NS_OK;

  // First, get buffered ranges for sub-readers.
  ReentrantMonitorConditionallyEnter mon(!mDecoder->OnDecodeThread(),
                                         mDecoder->GetReentrantMonitor());
  if (mAudioReader) {
    decoder = mAudioReader->GetDecoder();
    NS_ENSURE_TRUE(decoder, NS_ERROR_NULL_POINTER);
    resource = decoder->GetResource();
    NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
    resource->Pin();
    rv = mAudioReader->GetBuffered(&audioBuffered, aStartTime);
    NS_ENSURE_SUCCESS(rv, rv);
    resource->Unpin();
    rv = audioBuffered.GetLength(&audioRangeCount);
    NS_ENSURE_SUCCESS(rv, rv);
  }
  if (mVideoReader) {
    decoder = mVideoReader->GetDecoder();
    NS_ENSURE_TRUE(decoder, NS_ERROR_NULL_POINTER);
    resource = decoder->GetResource();
    NS_ENSURE_TRUE(resource, NS_ERROR_NULL_POINTER);
    resource->Pin();
    rv = mVideoReader->GetBuffered(&videoBuffered, aStartTime);
    NS_ENSURE_SUCCESS(rv, rv);
    resource->Unpin();
    rv = videoBuffered.GetLength(&videoRangeCount);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  // Now determine buffered data for available sub-readers.
  if (mAudioReader && mVideoReader) {
    // Calculate intersecting ranges.
    for (uint32_t i = 0; i < audioRangeCount; i++) {
      // |A|udio, |V|ideo, |I|ntersect.
      double startA, startV, startI;
      double endA, endV, endI;
      rv = audioBuffered.Start(i, &startA);
      NS_ENSURE_SUCCESS(rv, rv);
      rv = audioBuffered.End(i, &endA);
      NS_ENSURE_SUCCESS(rv, rv);

      for (uint32_t j = 0; j < videoRangeCount; j++) {
        rv = videoBuffered.Start(i, &startV);
        NS_ENSURE_SUCCESS(rv, rv);
        rv = videoBuffered.End(i, &endV);
        NS_ENSURE_SUCCESS(rv, rv);

        // If video block is before audio block, compare next video block.
        if (startA > endV) {
          continue;
        // If video block is after audio block, all of them are; compare next
        // audio block.
        } else if (endA < startV) {
          break;
        }
        // Calculate intersections of current audio and video blocks.
        startI = (startA > startV) ? startA : startV;
        endI = (endA > endV) ? endV : endA;
        aBuffered->Add(startI, endI);
      }
    }
  } else if (mAudioReader) {
    *aBuffered = audioBuffered;
  } else if (mVideoReader) {
    *aBuffered = videoBuffered;
  } else {
    return NS_ERROR_NOT_INITIALIZED;
  }

  return NS_OK;
}
Exemple #28
0
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;
}
Exemple #29
0
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;
}
Exemple #30
0
void GStreamerReader::ReadAndPushData(guint aLength)
{
  MediaResource* resource = mDecoder->GetResource();
  NS_ASSERTION(resource, "Decoder has no media resource");
  int64_t offset1 = resource->Tell();
  unused << offset1;
  nsresult rv = NS_OK;

  GstBuffer* buffer = gst_buffer_new_and_alloc(aLength);
#if GST_VERSION_MAJOR >= 1
  GstMapInfo info;
  gst_buffer_map(buffer, &info, GST_MAP_WRITE);
  guint8 *data = info.data;
#else
  guint8* data = GST_BUFFER_DATA(buffer);
#endif
  uint32_t size = 0, bytesRead = 0;
  while(bytesRead < aLength) {
    rv = resource->Read(reinterpret_cast<char*>(data + bytesRead),
        aLength - bytesRead, &size);
    if (NS_FAILED(rv) || size == 0)
      break;

    bytesRead += size;
  }

  int64_t offset2 = resource->Tell();
  unused << offset2;

#if GST_VERSION_MAJOR >= 1
  gst_buffer_unmap(buffer, &info);
  gst_buffer_set_size(buffer, bytesRead);
#else
  GST_BUFFER_SIZE(buffer) = bytesRead;
#endif

  GstFlowReturn ret = gst_app_src_push_buffer(mSource, gst_buffer_ref(buffer));
  if (ret != GST_FLOW_OK) {
    LOG(PR_LOG_ERROR, "ReadAndPushData push ret %s(%d)", gst_flow_get_name(ret), ret);
  }

  if (NS_FAILED(rv)) {
    /* Terminate the stream if there is an error in reading */
    LOG(PR_LOG_ERROR, "ReadAndPushData read error, rv=%x", rv);
    gst_app_src_end_of_stream(mSource);
  } else if (bytesRead < aLength) {
    /* If we read less than what we wanted, we reached the end */
    LOG(PR_LOG_WARNING, "ReadAndPushData read underflow, "
        "bytesRead=%u, aLength=%u, offset(%lld,%lld)",
        bytesRead, aLength, offset1, offset2);
    gst_app_src_end_of_stream(mSource);
  }

  gst_buffer_unref(buffer);

  /* Ensure offset change is consistent in this function.
   * If there are other stream operations on another thread at the same time,
   * it will disturb the GStreamer state machine.
   */
  MOZ_ASSERT(offset1 + bytesRead == offset2);
}