Attachment #520042: Patch 4: Ensure Ogg GetBuffered() is threadsafe for bug #639391

View | Details | Raw Unified | Return to bug 639391
Collapse All | Expand All

(-)a/content/media/ogg/nsOggReader.cpp (-52 / +42 lines)
Line     Link Here 
 Lines 1023-1072   PRInt64 nsOggReader::FindEndTime(PRInt64 Link Here 
1023
  return endTime;
1023
  return endTime;
1024
}
1024
}
1025
1025
1026
nsresult nsOggReader::GetSeekRanges(nsTArray<SeekRange>& aRanges)
1026
nsresult nsOggReader::GetSeekRanges(nsTArray<SeekRange>& aRanges)
1027
{
1027
{
1028
  NS_ASSERTION(mDecoder->OnStateMachineThread(),
1028
  NS_ASSERTION(mDecoder->OnStateMachineThread(),
1029
               "Should be on state machine thread.");
1029
               "Should be on state machine thread.");
1030
  mMonitor.AssertCurrentThreadIn();
1030
  mMonitor.AssertCurrentThreadIn();
1031
  PRInt64 startOffset = mDataOffset;
1031
  nsTArray<nsByteRange> cached;
1032
  nsMediaStream* stream = mDecoder->GetCurrentStream();
1032
  nsresult res = mDecoder->GetCurrentStream()->GetCachedRanges(cached);
1033
  while (PR_TRUE) {
1033
  NS_ENSURE_SUCCESS(res, res);
1034
    PRInt64 endOffset = stream->GetCachedDataEnd(startOffset);
1034
1035
    if (endOffset == startOffset) {
1035
  for (PRUint32 index = 0; index < aRanges.Length(); index++) {
1036
      // Uncached at startOffset.
1036
    nsByteRange& range = cached[index];
1037
      endOffset = stream->GetNextCachedData(startOffset);
1037
    PRInt64 startTime = -1;
1038
      if (endOffset == -1) {
1038
    PRInt64 endTime = -1;
1039
        // Uncached at startOffset until endOffset of stream, or we're at
1039
    if (NS_FAILED(ResetDecode())) {
1040
        // the end of stream.
1040
      return NS_ERROR_FAILURE;
1041
        break;
1042
      }
1043
    } else {
1044
      // Bytes [startOffset..endOffset] are cached.
1045
      PRInt64 startTime = -1;
1046
      PRInt64 endTime = -1;
1047
      if (NS_FAILED(ResetDecode())) {
1048
        return NS_ERROR_FAILURE;
1049
      }
1050
      FindStartTime(startOffset, startTime);
1051
      if (startTime != -1 &&
1052
          ((endTime = FindEndTime(endOffset)) != -1))
1053
      {
1054
        NS_ASSERTION(startOffset < endOffset,
1055
                     "Start offset must be before end offset");
1056
        NS_ASSERTION(startTime < endTime,
1057
                     "Start time must be before end time");
1058
        aRanges.AppendElement(SeekRange(startOffset,
1059
                                        endOffset,
1060
                                        startTime,
1061
                                        endTime));
1062
      }
1063
    }
1041
    }
1064
    startOffset = endOffset;
1042
    // Ensure the offsets are after the header pages.
1043
    PRInt64 startOffset = NS_MAX(cached[index].mStart, mDataOffset);
1044
    PRInt64 endOffset = NS_MAX(cached[index].mEnd, mDataOffset);
1045
1046
    FindStartTime(startOffset, startTime);
1047
    if (startTime != -1 &&
1048
        ((endTime = FindEndTime(endOffset)) != -1))
1049
    {
1050
      NS_ASSERTION(startTime < endTime,
1051
                   "Start time must be before end time");
1052
      aRanges.AppendElement(SeekRange(startOffset,
1053
                                      endOffset,
1054
                                      startTime,
1055
                                      endTime));
1056
     }
1065
  }
1057
  }
1066
  if (NS_FAILED(ResetDecode())) {
1058
  if (NS_FAILED(ResetDecode())) {
1067
    return NS_ERROR_FAILURE;
1059
    return NS_ERROR_FAILURE;
1068
  }
1060
  }
1069
  return NS_OK;
1061
  return NS_OK;
1070
}
1062
}
1071
1063
1072
nsOggReader::SeekRange
1064
nsOggReader::SeekRange
 Lines 1681-1721   nsresult nsOggReader::GetBuffered(nsTime Link Here 
1681
  // after metadata is read and GetBuffered isn't called before metadata is
1673
  // after metadata is read and GetBuffered isn't called before metadata is
1682
  // read.
1674
  // read.
1683
  if (!mInfo.mHasVideo && !mInfo.mHasAudio) {
1675
  if (!mInfo.mHasVideo && !mInfo.mHasAudio) {
1684
    // No need to search through the file if there are no audio or video tracks
1676
    // No need to search through the file if there are no audio or video tracks
1685
    return NS_OK;
1677
    return NS_OK;
1686
  }
1678
  }
1687
1679
1688
  nsMediaStream* stream = mDecoder->GetCurrentStream();
1680
  nsMediaStream* stream = mDecoder->GetCurrentStream();
1681
  nsTArray<nsByteRange> ranges;
1682
  nsresult res = stream->GetCachedRanges(ranges);
1683
  NS_ENSURE_SUCCESS(res, res);
1689
1684
1690
  // Traverse across the buffered byte ranges, determining the time ranges
1685
  // Traverse across the buffered byte ranges, determining the time ranges
1691
  // they contain. nsMediaStream::GetNextCachedData(offset) returns -1 when
1686
  // they contain. nsMediaStream::GetNextCachedData(offset) returns -1 when
1692
  // offset is after the end of the media stream, or there's no more cached
1687
  // offset is after the end of the media stream, or there's no more cached
1693
  // data after the offset. This loop will run until we've checked every
1688
  // data after the offset. This loop will run until we've checked every
1694
  // buffered range in the media, in increasing order of offset.
1689
  // buffered range in the media, in increasing order of offset.
1695
  ogg_sync_state state;
1690
  ogg_sync_state state;
1696
  ogg_sync_init(&state);
1691
  ogg_sync_init(&state);
1697
  PRInt64 startOffset = stream->GetNextCachedData(mDataOffset);
1692
  for (PRUint32 index = 0; index < ranges.Length(); index++) {
1698
  while (startOffset >= 0) {
1693
    // Ensure the offsets are after the header pages.
1699
    PRInt64 endOffset = stream->GetCachedDataEnd(startOffset);
1694
    PRInt64 startOffset = NS_MAX(ranges[index].mStart, mDataOffset);
1700
    NS_ASSERTION(startOffset < endOffset, "Buffered range must end after its start");
1695
    PRInt64 endOffset = NS_MAX(ranges[index].mEnd, mDataOffset);
1701
    // Bytes [startOffset..endOffset] are cached.
1702
1696
1703
    // Find the start time of the range.
1697
    // Because the granulepos time is actually the end time of the page,
1704
    PRInt64 startTime = -1;
1698
    // we special-case (startOffset == mDataOffset) so that the first
1705
    if (startOffset == mDataOffset) {
1699
    // buffered range always appears to be buffered from the media start
1706
      // Because the granulepos time is actually the end time of the page,
1700
    // time, rather than from the end-time of the first page.
1707
      // we special-case (startOffset == mDataOffset) so that the first
1701
    PRInt64 startTime = (startOffset == mDataOffset) ? aStartTime : -1;
1708
      // buffered range always appears to be buffered from [t=0...] rather
1702
1709
      // than from the end-time of the first page.
1703
    // Find the start time of the range. Read pages until we find one with a
1710
      startTime = aStartTime;
1704
    // granulepos which we can convert into a timestamp to use as the time of
1711
    }
1705
    // the start of the buffered range.
1712
    // Read pages until we find one with a granulepos which we can convert
1713
    // into a timestamp to use as the time of the start of the buffered range.
1714
    ogg_sync_reset(&state);
1706
    ogg_sync_reset(&state);
1715
    while (startTime == -1) {
1707
    while (startTime == -1) {
1716
      ogg_page page;
1708
      ogg_page page;
1717
      PRInt32 discard;
1709
      PRInt32 discard;
1718
      PageSyncResult res = PageSync(stream,
1710
      PageSyncResult res = PageSync(stream,
1719
                                    &state,
1711
                                    &state,
1720
                                    PR_TRUE,
1712
                                    PR_TRUE,
1721
                                    startOffset,
1713
                                    startOffset,
 Lines 1753-1785   nsresult nsOggReader::GetBuffered(nsTime Link Here 
1753
        // Stream is not the theora or vorbis stream we're playing,
1745
        // Stream is not the theora or vorbis stream we're playing,
1754
        // but is one that we have header data for.
1746
        // but is one that we have header data for.
1755
        startOffset += page.header_len + page.body_len;
1747
        startOffset += page.header_len + page.body_len;
1756
        continue;
1748
        continue;
1757
      }
1749
      }
1758
      else {
1750
      else {
1759
        // Page is for a stream we don't know about (possibly a chained
1751
        // Page is for a stream we don't know about (possibly a chained
1760
        // ogg), return an error.
1752
        // ogg), return an error.
1753
        ogg_sync_clear(&state);
1761
        return PAGE_SYNC_ERROR;
1754
        return PAGE_SYNC_ERROR;
1762
      }
1755
      }
1763
    }
1756
    }
1764
1757
1765
    if (startTime != -1) {
1758
    if (startTime != -1) {
1766
      // We were able to find a start time for that range, see if we can
1759
      // We were able to find a start time for that range, see if we can
1767
      // find an end time.
1760
      // find an end time.
1768
      PRInt64 endTime = FindEndTime(startOffset, endOffset, PR_TRUE, &state);
1761
      PRInt64 endTime = FindEndTime(startOffset, endOffset, PR_TRUE, &state);
1769
      if (endTime != -1) {
1762
      if (endTime != -1) {
1770
        endTime -= aStartTime;
1763
        endTime -= aStartTime;
1771
        aBuffered->Add(static_cast<double>(startTime) / 1000.0,
1764
        aBuffered->Add(static_cast<double>(startTime) / 1000.0,
1772
                       static_cast<double>(endTime) / 1000.0);
1765
                       static_cast<double>(endTime) / 1000.0);
1773
      }
1766
      }
1774
    }
1767
    }
1775
    startOffset = stream->GetNextCachedData(endOffset);
1776
    NS_ASSERTION(startOffset == -1 || startOffset > endOffset,
1777
      "Must have advanced to start of next range, or hit end of stream");
1778
  }
1768
  }
1779
1769
1780
  // If we don't clear the sync state before exit we'll leak.
1770
  // If we don't clear the sync state before exit we'll leak.
1781
  ogg_sync_clear(&state);
1771
  ogg_sync_clear(&state);
1782
1772
1783
  return NS_OK;
1773
  return NS_OK;
1784
}
1774
}
1785
1775

Return to bug 639391