Commit 29dcc87a authored by vrk@google.com's avatar vrk@google.com

Cap sourceBuffered() on duration and truncate duration on EoS

Adds logic + test to truncate the duration on Media Source streams when
EndOfStream is signalled. Also adds some logic to make sure buffered streams
never exceed the reported duration.

BUG=139899

Review URL: https://chromiumcodereview.appspot.com/10829108

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149702 0039d316-1c4b-4281-b951-d872f2087c98
parent a9d31ef4
...@@ -64,9 +64,6 @@ class MEDIA_EXPORT DemuxerStream ...@@ -64,9 +64,6 @@ class MEDIA_EXPORT DemuxerStream
// if type() != VIDEO. // if type() != VIDEO.
virtual const VideoDecoderConfig& video_decoder_config() = 0; virtual const VideoDecoderConfig& video_decoder_config() = 0;
// Returns time ranges known to have been seen by this stream.
virtual Ranges<base::TimeDelta> GetBufferedRanges() = 0;
// Returns the type of stream. // Returns the type of stream.
virtual Type type() = 0; virtual Type type() = 0;
......
...@@ -39,7 +39,7 @@ class Ranges { ...@@ -39,7 +39,7 @@ class Ranges {
void clear(); void clear();
// Computes the intersection between this range and |other|. // Computes the intersection between this range and |other|.
Ranges<T> IntersectionWith(const Ranges<T>& other); Ranges<T> IntersectionWith(const Ranges<T>& other) const;
private: private:
// Wrapper around DCHECK_LT allowing comparisons of operator<<'able T's. // Wrapper around DCHECK_LT allowing comparisons of operator<<'able T's.
...@@ -133,7 +133,7 @@ void Ranges<T>::clear() { ...@@ -133,7 +133,7 @@ void Ranges<T>::clear() {
} }
template<class T> template<class T>
Ranges<T> Ranges<T>::IntersectionWith(const Ranges<T>& other) { Ranges<T> Ranges<T>::IntersectionWith(const Ranges<T>& other) const {
Ranges<T> result; Ranges<T> result;
size_t i = 0; size_t i = 0;
......
...@@ -176,6 +176,9 @@ class ChunkDemuxerStream : public DemuxerStream { ...@@ -176,6 +176,9 @@ class ChunkDemuxerStream : public DemuxerStream {
// Returns true if buffers were successfully added. // Returns true if buffers were successfully added.
bool Append(const StreamParser::BufferQueue& buffers); bool Append(const StreamParser::BufferQueue& buffers);
// Returns the range of buffered data in this stream, capped at |duration|.
Ranges<TimeDelta> GetBufferedRanges(base::TimeDelta duration) const;
// Signal to the stream that buffers handed in through subsequent calls to // Signal to the stream that buffers handed in through subsequent calls to
// Append() belong to a media segment that starts at |start_timestamp|. // Append() belong to a media segment that starts at |start_timestamp|.
void OnNewMediaSegment(TimeDelta start_timestamp); void OnNewMediaSegment(TimeDelta start_timestamp);
...@@ -199,7 +202,6 @@ class ChunkDemuxerStream : public DemuxerStream { ...@@ -199,7 +202,6 @@ class ChunkDemuxerStream : public DemuxerStream {
virtual void EnableBitstreamConverter() OVERRIDE; virtual void EnableBitstreamConverter() OVERRIDE;
virtual const AudioDecoderConfig& audio_decoder_config() OVERRIDE; virtual const AudioDecoderConfig& audio_decoder_config() OVERRIDE;
virtual const VideoDecoderConfig& video_decoder_config() OVERRIDE; virtual const VideoDecoderConfig& video_decoder_config() OVERRIDE;
virtual Ranges<TimeDelta> GetBufferedRanges() OVERRIDE;
protected: protected:
virtual ~ChunkDemuxerStream(); virtual ~ChunkDemuxerStream();
...@@ -318,9 +320,20 @@ bool ChunkDemuxerStream::Append(const StreamParser::BufferQueue& buffers) { ...@@ -318,9 +320,20 @@ bool ChunkDemuxerStream::Append(const StreamParser::BufferQueue& buffers) {
return true; return true;
} }
Ranges<TimeDelta> ChunkDemuxerStream::GetBufferedRanges() { Ranges<TimeDelta> ChunkDemuxerStream::GetBufferedRanges(
base::TimeDelta duration) const {
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
return stream_->GetBufferedTime(); Ranges<TimeDelta> range = stream_->GetBufferedTime();
if (range.size() == 0u)
return range;
// Clamp the end of the stream's buffered ranges to fit within the duration.
// This can be done by intersecting the stream's range with the valid time
// range.
Ranges<TimeDelta> valid_time_range;
valid_time_range.Add(range.start(0), duration);
return range.IntersectionWith(valid_time_range);
} }
bool ChunkDemuxerStream::UpdateAudioConfig(const AudioDecoderConfig& config) { bool ChunkDemuxerStream::UpdateAudioConfig(const AudioDecoderConfig& config) {
...@@ -671,12 +684,12 @@ Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const { ...@@ -671,12 +684,12 @@ Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const {
if (id == source_id_audio_ && id != source_id_video_) { if (id == source_id_audio_ && id != source_id_video_) {
// Only include ranges that have been buffered in |audio_| // Only include ranges that have been buffered in |audio_|
return audio_ ? audio_->GetBufferedRanges() : Ranges<TimeDelta>(); return audio_ ? audio_->GetBufferedRanges(duration_) : Ranges<TimeDelta>();
} }
if (id != source_id_audio_ && id == source_id_video_) { if (id != source_id_audio_ && id == source_id_video_) {
// Only include ranges that have been buffered in |video_| // Only include ranges that have been buffered in |video_|
return video_ ? video_->GetBufferedRanges() : Ranges<TimeDelta>(); return video_ ? video_->GetBufferedRanges(duration_) : Ranges<TimeDelta>();
} }
return ComputeIntersection(); return ComputeIntersection();
...@@ -689,8 +702,8 @@ Ranges<TimeDelta> ChunkDemuxer::ComputeIntersection() const { ...@@ -689,8 +702,8 @@ Ranges<TimeDelta> ChunkDemuxer::ComputeIntersection() const {
return Ranges<TimeDelta>(); return Ranges<TimeDelta>();
// Include ranges that have been buffered in both |audio_| and |video_|. // Include ranges that have been buffered in both |audio_| and |video_|.
Ranges<TimeDelta> audio_ranges = audio_->GetBufferedRanges(); Ranges<TimeDelta> audio_ranges = audio_->GetBufferedRanges(duration_);
Ranges<TimeDelta> video_ranges = video_->GetBufferedRanges(); Ranges<TimeDelta> video_ranges = video_->GetBufferedRanges(duration_);
Ranges<TimeDelta> result = audio_ranges.IntersectionWith(video_ranges); Ranges<TimeDelta> result = audio_ranges.IntersectionWith(video_ranges);
if (state_ == ENDED && result.size() > 0) { if (state_ == ENDED && result.size() > 0) {
...@@ -763,13 +776,7 @@ bool ChunkDemuxer::AppendData(const std::string& id, ...@@ -763,13 +776,7 @@ bool ChunkDemuxer::AppendData(const std::string& id,
std::swap(cb, seek_cb_); std::swap(cb, seek_cb_);
} }
if (audio_ && !video_) { ranges = GetBufferedRanges();
ranges = audio_->GetBufferedRanges();
} else if (!audio_ && video_) {
ranges = video_->GetBufferedRanges();
} else {
ranges = ComputeIntersection();
}
} }
for (size_t i = 0; i < ranges.size(); ++i) for (size_t i = 0; i < ranges.size(); ++i)
...@@ -825,10 +832,12 @@ bool ChunkDemuxer::EndOfStream(PipelineStatus status) { ...@@ -825,10 +832,12 @@ bool ChunkDemuxer::EndOfStream(PipelineStatus status) {
if (video_) if (video_)
video_->EndOfStream(); video_->EndOfStream();
if (status != PIPELINE_OK) if (status != PIPELINE_OK) {
ReportError_Locked(status); ReportError_Locked(status);
else } else {
ChangeState_Locked(ENDED); ChangeState_Locked(ENDED);
DecreaseDurationIfNecessary();
}
return true; return true;
} }
...@@ -1121,7 +1130,7 @@ void ChunkDemuxer::IncreaseDurationIfNecessary( ...@@ -1121,7 +1130,7 @@ void ChunkDemuxer::IncreaseDurationIfNecessary(
if (buffers.back()->GetTimestamp() <= duration_) if (buffers.back()->GetTimestamp() <= duration_)
return; return;
Ranges<TimeDelta> ranges = stream->GetBufferedRanges(); Ranges<TimeDelta> ranges = stream->GetBufferedRanges(kInfiniteDuration());
DCHECK_GT(ranges.size(), 0u); DCHECK_GT(ranges.size(), 0u);
base::TimeDelta last_timestamp_buffered = ranges.end(ranges.size() - 1); base::TimeDelta last_timestamp_buffered = ranges.end(ranges.size() - 1);
...@@ -1129,4 +1138,22 @@ void ChunkDemuxer::IncreaseDurationIfNecessary( ...@@ -1129,4 +1138,22 @@ void ChunkDemuxer::IncreaseDurationIfNecessary(
UpdateDuration(last_timestamp_buffered); UpdateDuration(last_timestamp_buffered);
} }
void ChunkDemuxer::DecreaseDurationIfNecessary() {
Ranges<TimeDelta> ranges = GetBufferedRanges();
if (ranges.size() == 0u)
return;
base::TimeDelta last_timestamp_buffered = ranges.end(ranges.size() - 1);
if (last_timestamp_buffered < duration_)
UpdateDuration(last_timestamp_buffered);
}
Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges() const {
if (audio_ && !video_)
return audio_->GetBufferedRanges(duration_);
else if (!audio_ && video_)
return video_->GetBufferedRanges(duration_);
return ComputeIntersection();
}
} // namespace media } // namespace media
...@@ -143,9 +143,16 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { ...@@ -143,9 +143,16 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
const StreamParser::BufferQueue& buffers, const StreamParser::BufferQueue& buffers,
const scoped_refptr<ChunkDemuxerStream>& stream); const scoped_refptr<ChunkDemuxerStream>& stream);
// Decreases |duration_| if the buffered region is less than |duration_| when
// EndOfStream() is called.
void DecreaseDurationIfNecessary();
// Sets |duration_| to |new_duration| and notifies |host_|. // Sets |duration_| to |new_duration| and notifies |host_|.
void UpdateDuration(base::TimeDelta new_duration); void UpdateDuration(base::TimeDelta new_duration);
// Returns the ranges representing the buffered data in the demuxer.
Ranges<base::TimeDelta> GetBufferedRanges() const;
mutable base::Lock lock_; mutable base::Lock lock_;
State state_; State state_;
......
...@@ -47,6 +47,8 @@ static const int kVideoBlockDuration = 33; ...@@ -47,6 +47,8 @@ static const int kVideoBlockDuration = 33;
static const char* kSourceId = "SourceId"; static const char* kSourceId = "SourceId";
static const char* kDefaultFirstClusterRange = "{ [0,46) }"; static const char* kDefaultFirstClusterRange = "{ [0,46) }";
static const int kDefaultFirstClusterEndTimestamp = 66;
static const int kDefaultSecondClusterEndTimestamp = 132;
base::TimeDelta kDefaultDuration() { base::TimeDelta kDefaultDuration() {
return base::TimeDelta::FromMilliseconds(201224); return base::TimeDelta::FromMilliseconds(201224);
...@@ -1100,6 +1102,8 @@ TEST_F(ChunkDemuxerTest, TestEndOfStreamWithPendingReads) { ...@@ -1100,6 +1102,8 @@ TEST_F(ChunkDemuxerTest, TestEndOfStreamWithPendingReads) {
end_of_stream_helper_1.CheckIfReadDonesWereCalled(false); end_of_stream_helper_1.CheckIfReadDonesWereCalled(false);
end_of_stream_helper_2.CheckIfReadDonesWereCalled(false); end_of_stream_helper_2.CheckIfReadDonesWereCalled(false);
EXPECT_CALL(host_, SetDuration(
base::TimeDelta::FromMilliseconds(kVideoBlockDuration)));
demuxer_->EndOfStream(PIPELINE_OK); demuxer_->EndOfStream(PIPELINE_OK);
end_of_stream_helper_1.CheckIfReadDonesWereCalled(true); end_of_stream_helper_1.CheckIfReadDonesWereCalled(true);
...@@ -1139,6 +1143,8 @@ TEST_F(ChunkDemuxerTest, TestReadsAfterEndOfStream) { ...@@ -1139,6 +1143,8 @@ TEST_F(ChunkDemuxerTest, TestReadsAfterEndOfStream) {
EXPECT_TRUE(video_read_done_1); EXPECT_TRUE(video_read_done_1);
end_of_stream_helper_1.CheckIfReadDonesWereCalled(false); end_of_stream_helper_1.CheckIfReadDonesWereCalled(false);
EXPECT_CALL(host_, SetDuration(
base::TimeDelta::FromMilliseconds(kVideoBlockDuration)));
EXPECT_TRUE(demuxer_->EndOfStream(PIPELINE_OK)); EXPECT_TRUE(demuxer_->EndOfStream(PIPELINE_OK));
end_of_stream_helper_1.CheckIfReadDonesWereCalled(true); end_of_stream_helper_1.CheckIfReadDonesWereCalled(true);
...@@ -1697,6 +1703,7 @@ TEST_F(ChunkDemuxerTest, GetBufferedRanges_EndOfStream) { ...@@ -1697,6 +1703,7 @@ TEST_F(ChunkDemuxerTest, GetBufferedRanges_EndOfStream) {
CheckExpectedRanges("{ [0,90) }"); CheckExpectedRanges("{ [0,90) }");
EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(100)));
demuxer_->EndOfStream(PIPELINE_OK); demuxer_->EndOfStream(PIPELINE_OK);
CheckExpectedRanges("{ [0,100) }"); CheckExpectedRanges("{ [0,100) }");
...@@ -1813,6 +1820,7 @@ TEST_F(ChunkDemuxerTest, TestEndOfStreamFailures) { ...@@ -1813,6 +1820,7 @@ TEST_F(ChunkDemuxerTest, TestEndOfStreamFailures) {
// Make sure that end of stream fails because there is a gap between // Make sure that end of stream fails because there is a gap between
// the current position(0) and the end of the appended data. // the current position(0) and the end of the appended data.
EXPECT_CALL(host_, SetDuration(base::TimeDelta::FromMilliseconds(50)));
ASSERT_FALSE(demuxer_->EndOfStream(PIPELINE_OK)); ASSERT_FALSE(demuxer_->EndOfStream(PIPELINE_OK));
// Seek to an time that is inside the last ranges for both streams // Seek to an time that is inside the last ranges for both streams
...@@ -1863,6 +1871,8 @@ TEST_F(ChunkDemuxerTest, TestEndOfStreamDuringSeek) { ...@@ -1863,6 +1871,8 @@ TEST_F(ChunkDemuxerTest, TestEndOfStreamDuringSeek) {
demuxer_->StartWaitingForSeek(); demuxer_->StartWaitingForSeek();
ASSERT_TRUE(AppendData(cluster_b->data(), cluster_b->size())); ASSERT_TRUE(AppendData(cluster_b->data(), cluster_b->size()));
EXPECT_CALL(host_, SetDuration(
base::TimeDelta::FromMilliseconds(kDefaultSecondClusterEndTimestamp)));
demuxer_->EndOfStream(PIPELINE_OK); demuxer_->EndOfStream(PIPELINE_OK);
demuxer_->Seek(base::TimeDelta::FromSeconds(0), demuxer_->Seek(base::TimeDelta::FromSeconds(0),
...@@ -2133,20 +2143,36 @@ TEST_F(ChunkDemuxerTest, TestTimestampOffsetMidParse) { ...@@ -2133,20 +2143,36 @@ TEST_F(ChunkDemuxerTest, TestTimestampOffsetMidParse) {
TEST_F(ChunkDemuxerTest, TestDurationChange) { TEST_F(ChunkDemuxerTest, TestDurationChange) {
ASSERT_TRUE(InitDemuxer(true, true, false)); ASSERT_TRUE(InitDemuxer(true, true, false));
static const int kStreamDuration = kDefaultDuration().InMilliseconds();
// Add data leading up to the currently set duration. // Add data leading up to the currently set duration.
scoped_ptr<Cluster> first_cluster = GenerateCluster( scoped_ptr<Cluster> first_cluster = GenerateCluster(
kDefaultDuration().InMilliseconds() - kAudioBlockDuration, kStreamDuration - kAudioBlockDuration,
kDefaultDuration().InMilliseconds() - kVideoBlockDuration, 2); kStreamDuration - kVideoBlockDuration, 2);
ASSERT_TRUE(AppendData(first_cluster->data(), first_cluster->size())); ASSERT_TRUE(AppendData(first_cluster->data(), first_cluster->size()));
// Now add data past the duration and expect a new duration to be signalled. CheckExpectedRanges(kSourceId, "{ [201191,201224) }");
// Add data at the currently set duration. The duration should not increase.
scoped_ptr<Cluster> second_cluster = GenerateCluster( scoped_ptr<Cluster> second_cluster = GenerateCluster(
kDefaultDuration().InMilliseconds(), 4); kDefaultDuration().InMilliseconds(), 2);
EXPECT_CALL(host_, SetDuration(
kDefaultDuration() + base::TimeDelta::FromMilliseconds(
kAudioBlockDuration * 2)));
ASSERT_TRUE(AppendData(second_cluster->data(), second_cluster->size())); ASSERT_TRUE(AppendData(second_cluster->data(), second_cluster->size()));
// Range should not be affected.
CheckExpectedRanges(kSourceId, "{ [201191,201224) }");
// Now add data past the duration and expect a new duration to be signalled.
static const int kNewStreamDuration =
kStreamDuration + kAudioBlockDuration * 2;
scoped_ptr<Cluster> third_cluster = GenerateCluster(
kStreamDuration + kAudioBlockDuration,
kStreamDuration + kVideoBlockDuration, 2);
EXPECT_CALL(host_, SetDuration(
base::TimeDelta::FromMilliseconds(kNewStreamDuration)));
ASSERT_TRUE(AppendData(third_cluster->data(), third_cluster->size()));
// See that the range has increased appropriately.
CheckExpectedRanges(kSourceId, "{ [201191,201270) }");
} }
TEST_F(ChunkDemuxerTest, TestDurationChangeTimestampOffset) { TEST_F(ChunkDemuxerTest, TestDurationChangeTimestampOffset) {
...@@ -2162,4 +2188,15 @@ TEST_F(ChunkDemuxerTest, TestDurationChangeTimestampOffset) { ...@@ -2162,4 +2188,15 @@ TEST_F(ChunkDemuxerTest, TestDurationChangeTimestampOffset) {
ASSERT_TRUE(AppendData(cluster->data(), cluster->size())); ASSERT_TRUE(AppendData(cluster->data(), cluster->size()));
} }
TEST_F(ChunkDemuxerTest, TestEndOfStreamTruncateDuration) {
ASSERT_TRUE(InitDemuxer(true, true, false));
scoped_ptr<Cluster> cluster_a(kDefaultFirstCluster());
ASSERT_TRUE(AppendData(cluster_a->data(), cluster_a->size()));
EXPECT_CALL(host_, SetDuration(
base::TimeDelta::FromMilliseconds(kDefaultFirstClusterEndTimestamp)));
demuxer_->EndOfStream(PIPELINE_OK);
}
} // namespace media } // namespace media
...@@ -32,10 +32,6 @@ void DummyDemuxerStream::Read(const ReadCB& read_cb) {} ...@@ -32,10 +32,6 @@ void DummyDemuxerStream::Read(const ReadCB& read_cb) {}
void DummyDemuxerStream::EnableBitstreamConverter() {} void DummyDemuxerStream::EnableBitstreamConverter() {}
Ranges<base::TimeDelta> DummyDemuxerStream::GetBufferedRanges() {
return Ranges<base::TimeDelta>();
}
DummyDemuxer::DummyDemuxer(bool has_video, bool has_audio) { DummyDemuxer::DummyDemuxer(bool has_video, bool has_audio) {
streams_.resize(DemuxerStream::NUM_TYPES); streams_.resize(DemuxerStream::NUM_TYPES);
if (has_audio) if (has_audio)
......
...@@ -28,7 +28,6 @@ class DummyDemuxerStream : public DemuxerStream { ...@@ -28,7 +28,6 @@ class DummyDemuxerStream : public DemuxerStream {
virtual const AudioDecoderConfig& audio_decoder_config() OVERRIDE; virtual const AudioDecoderConfig& audio_decoder_config() OVERRIDE;
virtual const VideoDecoderConfig& video_decoder_config() OVERRIDE; virtual const VideoDecoderConfig& video_decoder_config() OVERRIDE;
virtual void EnableBitstreamConverter() OVERRIDE; virtual void EnableBitstreamConverter() OVERRIDE;
virtual Ranges<base::TimeDelta> GetBufferedRanges() OVERRIDE;
protected: protected:
virtual ~DummyDemuxerStream(); virtual ~DummyDemuxerStream();
......
...@@ -237,7 +237,7 @@ base::TimeDelta FFmpegDemuxerStream::GetElapsedTime() const { ...@@ -237,7 +237,7 @@ base::TimeDelta FFmpegDemuxerStream::GetElapsedTime() const {
return ConvertStreamTimestamp(stream_->time_base, stream_->cur_dts); return ConvertStreamTimestamp(stream_->time_base, stream_->cur_dts);
} }
Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() { Ranges<base::TimeDelta> FFmpegDemuxerStream::GetBufferedRanges() const {
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
return buffered_ranges_; return buffered_ranges_;
} }
...@@ -323,7 +323,12 @@ void FFmpegDemuxer::Initialize(DemuxerHost* host, ...@@ -323,7 +323,12 @@ void FFmpegDemuxer::Initialize(DemuxerHost* host,
scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream( scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream(
DemuxerStream::Type type) { DemuxerStream::Type type) {
StreamVector::iterator iter; return GetFFmpegStream(type);
}
scoped_refptr<FFmpegDemuxerStream> FFmpegDemuxer::GetFFmpegStream(
DemuxerStream::Type type) const {
StreamVector::const_iterator iter;
for (iter = streams_.begin(); iter != streams_.end(); ++iter) { for (iter = streams_.begin(); iter != streams_.end(); ++iter) {
if (*iter && (*iter)->type() == type) { if (*iter && (*iter)->type() == type) {
return *iter; return *iter;
...@@ -717,9 +722,10 @@ void FFmpegDemuxer::SignalReadCompleted(int size) { ...@@ -717,9 +722,10 @@ void FFmpegDemuxer::SignalReadCompleted(int size) {
void FFmpegDemuxer::NotifyBufferingChanged() { void FFmpegDemuxer::NotifyBufferingChanged() {
DCHECK_EQ(MessageLoop::current(), message_loop_); DCHECK_EQ(MessageLoop::current(), message_loop_);
Ranges<base::TimeDelta> buffered; Ranges<base::TimeDelta> buffered;
scoped_refptr<DemuxerStream> audio = scoped_refptr<FFmpegDemuxerStream> audio =
audio_disabled_ ? NULL : GetStream(DemuxerStream::AUDIO); audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO);
scoped_refptr<DemuxerStream> video = GetStream(DemuxerStream::VIDEO); scoped_refptr<FFmpegDemuxerStream> video =
GetFFmpegStream(DemuxerStream::VIDEO);
if (audio && video) { if (audio && video) {
buffered = audio->GetBufferedRanges().IntersectionWith( buffered = audio->GetBufferedRanges().IntersectionWith(
video->GetBufferedRanges()); video->GetBufferedRanges());
......
...@@ -84,7 +84,9 @@ class FFmpegDemuxerStream : public DemuxerStream { ...@@ -84,7 +84,9 @@ class FFmpegDemuxerStream : public DemuxerStream {
virtual void EnableBitstreamConverter() OVERRIDE; virtual void EnableBitstreamConverter() OVERRIDE;
virtual const AudioDecoderConfig& audio_decoder_config() OVERRIDE; virtual const AudioDecoderConfig& audio_decoder_config() OVERRIDE;
virtual const VideoDecoderConfig& video_decoder_config() OVERRIDE; virtual const VideoDecoderConfig& video_decoder_config() OVERRIDE;
virtual Ranges<base::TimeDelta> GetBufferedRanges() OVERRIDE;
// Returns the range of buffered data in this stream.
Ranges<base::TimeDelta> GetBufferedRanges() const;
// Returns elapsed time based on the already queued packets. // Returns elapsed time based on the already queued packets.
// Used to determine stream duration when it's not known ahead of time. // Used to determine stream duration when it's not known ahead of time.
...@@ -210,6 +212,11 @@ class MEDIA_EXPORT FFmpegDemuxer : public Demuxer, public FFmpegURLProtocol { ...@@ -210,6 +212,11 @@ class MEDIA_EXPORT FFmpegDemuxer : public Demuxer, public FFmpegURLProtocol {
// read or kReadError in case of error. // read or kReadError in case of error.
virtual void SignalReadCompleted(int size); virtual void SignalReadCompleted(int size);
// Returns the stream from |streams_| that matches |type| as an
// FFmpegDemuxerStream.
scoped_refptr<FFmpegDemuxerStream> GetFFmpegStream(
DemuxerStream::Type type) const;
DemuxerHost* host_; DemuxerHost* host_;
MessageLoop* message_loop_; MessageLoop* message_loop_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment