Index: media/filters/chunk_demuxer.cc |
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc |
index 1c875906ac7798b3022f0c51527c67911f9c4852..32d029af2b6cd8c673d410b1b4a3cbe8e98d88f9 100644 |
--- a/media/filters/chunk_demuxer.cc |
+++ b/media/filters/chunk_demuxer.cc |
@@ -15,6 +15,8 @@ |
#include "media/mp4/mp4_stream_parser.h" |
#include "media/webm/webm_stream_parser.h" |
+using base::TimeDelta; |
+ |
namespace media { |
struct CodecInfo { |
@@ -73,6 +75,13 @@ static const SupportedTypeInfo kSupportedTypeInfo[] = { |
{ "audio/mp4", &BuildMP4Parser, kAudioMP4Codecs }, |
}; |
+ |
+// The fake total size we use for converting times to bytes |
+// for AddBufferedByteRange() calls. |
+// TODO(acolwell): Remove this once Pipeline accepts buffered times |
+// instead of only buffered bytes. |
+enum { kFakeTotalBytes = 1000000 }; |
+ |
// Checks to see if the specified |type| and |codecs| list are supported. |
// Returns true if |type| and all codecs listed in |codecs| are supported. |
// |factory_function| contains a function that can build a StreamParser |
@@ -147,7 +156,7 @@ class ChunkDemuxerStream : public DemuxerStream { |
explicit ChunkDemuxerStream(const VideoDecoderConfig& video_config); |
void StartWaitingForSeek(); |
- void Seek(base::TimeDelta time); |
+ void Seek(TimeDelta time); |
bool IsSeekPending() const; |
void Flush(); |
@@ -157,11 +166,11 @@ class ChunkDemuxerStream : public DemuxerStream { |
bool Append(const StreamParser::BufferQueue& buffers); |
// Returns a list of the buffered time ranges. |
- SourceBufferStream::TimespanList GetBufferedTime() const; |
+ Ranges<TimeDelta> GetBufferedTime() const; |
// Signal to the stream that buffers handed in through subsequent calls to |
// Append() belong to a media segment that starts at |start_timestamp|. |
- void OnNewMediaSegment(base::TimeDelta start_timestamp); |
+ void OnNewMediaSegment(TimeDelta start_timestamp); |
// Called when mid-stream config updates occur. |
// Returns true if the new config is accepted. |
@@ -212,7 +221,7 @@ class ChunkDemuxerStream : public DemuxerStream { |
// The timestamp of the current media segment being parsed by |
// |stream_parser_|. |
- base::TimeDelta media_segment_start_time_; |
+ TimeDelta media_segment_start_time_; |
DISALLOW_IMPLICIT_CONSTRUCTORS(ChunkDemuxerStream); |
}; |
@@ -244,7 +253,7 @@ void ChunkDemuxerStream::StartWaitingForSeek() { |
it->Run(NULL); |
} |
-void ChunkDemuxerStream::Seek(base::TimeDelta time) { |
+void ChunkDemuxerStream::Seek(TimeDelta time) { |
base::AutoLock auto_lock(lock_); |
DCHECK(read_cbs_.empty()); |
@@ -264,7 +273,7 @@ void ChunkDemuxerStream::Flush() { |
media_segment_start_time_ = kNoTimestamp(); |
} |
-void ChunkDemuxerStream::OnNewMediaSegment(base::TimeDelta start_timestamp) { |
+void ChunkDemuxerStream::OnNewMediaSegment(TimeDelta start_timestamp) { |
media_segment_start_time_ = start_timestamp; |
} |
@@ -290,7 +299,7 @@ bool ChunkDemuxerStream::Append(const StreamParser::BufferQueue& buffers) { |
return true; |
} |
-SourceBufferStream::TimespanList ChunkDemuxerStream::GetBufferedTime() const { |
+Ranges<TimeDelta> ChunkDemuxerStream::GetBufferedTime() const { |
base::AutoLock auto_lock(lock_); |
return stream_->GetBufferedTime(); |
} |
@@ -463,8 +472,7 @@ void ChunkDemuxerStream::CreateReadDoneClosures_Locked(ClosureQueue* closures) { |
ChunkDemuxer::ChunkDemuxer(ChunkDemuxerClient* client) |
: state_(WAITING_FOR_INIT), |
host_(NULL), |
- client_(client), |
- buffered_bytes_(0) { |
+ client_(client) { |
DCHECK(client); |
} |
@@ -489,7 +497,7 @@ void ChunkDemuxer::Stop(const base::Closure& callback) { |
callback.Run(); |
} |
-void ChunkDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { |
+void ChunkDemuxer::Seek(TimeDelta time, const PipelineStatusCB& cb) { |
DVLOG(1) << "Seek(" << time.InSecondsF() << ")"; |
PipelineStatus status = PIPELINE_ERROR_INVALID_STATE; |
@@ -538,11 +546,11 @@ scoped_refptr<DemuxerStream> ChunkDemuxer::GetStream( |
return NULL; |
} |
-base::TimeDelta ChunkDemuxer::GetStartTime() const { |
+TimeDelta ChunkDemuxer::GetStartTime() const { |
DVLOG(1) << "GetStartTime()"; |
// TODO(acolwell) : Fix this so it uses the time on the first packet. |
// (crbug.com/132815) |
- return base::TimeDelta(); |
+ return TimeDelta(); |
} |
void ChunkDemuxer::StartWaitingForSeek() { |
@@ -627,99 +635,55 @@ void ChunkDemuxer::RemoveId(const std::string& id) { |
video_->Shutdown(); |
} |
-bool ChunkDemuxer::GetBufferedRanges(const std::string& id, |
- Ranges* ranges_out) const { |
+Ranges<TimeDelta> ChunkDemuxer::GetBufferedRanges(const std::string& id) const { |
DCHECK(!id.empty()); |
DCHECK_GT(stream_parser_map_.count(id), 0u); |
DCHECK(id == source_id_audio_ || id == source_id_video_); |
- DCHECK(ranges_out); |
base::AutoLock auto_lock(lock_); |
if (id == source_id_audio_ && id != source_id_video_) { |
// Only include ranges that have been buffered in |audio_| |
- return CopyIntoRanges(audio_->GetBufferedTime(), ranges_out); |
+ return audio_->GetBufferedTime(); |
} |
if (id != source_id_audio_ && id == source_id_video_) { |
// Only include ranges that have been buffered in |video_| |
- return CopyIntoRanges(video_->GetBufferedTime(), ranges_out); |
+ return video_->GetBufferedTime(); |
} |
- // Include ranges that have been buffered in both |audio_| and |video_|. |
- SourceBufferStream::TimespanList audio_ranges = audio_->GetBufferedTime(); |
- SourceBufferStream::TimespanList video_ranges = video_->GetBufferedTime(); |
- SourceBufferStream::TimespanList::const_iterator video_ranges_itr = |
- video_ranges.begin(); |
- SourceBufferStream::TimespanList::const_iterator audio_ranges_itr = |
- audio_ranges.begin(); |
- bool success = false; |
- |
- while (audio_ranges_itr != audio_ranges.end() && |
- video_ranges_itr != video_ranges.end()) { |
- // If this is the last range and EndOfStream() was called (i.e. all data |
- // has been appended), choose the max end point of the ranges. |
- bool last_range_after_ended = |
- state_ == ENDED && |
- (audio_ranges_itr + 1) == audio_ranges.end() && |
- (video_ranges_itr + 1) == video_ranges.end(); |
- |
- // Audio range start time is within the video range. |
- if ((*audio_ranges_itr).first >= (*video_ranges_itr).first && |
- (*audio_ranges_itr).first <= (*video_ranges_itr).second) { |
- AddIntersectionRange(*audio_ranges_itr, *video_ranges_itr, |
- last_range_after_ended, ranges_out); |
- audio_ranges_itr++; |
- success = true; |
- continue; |
- } |
+ return ComputeIntersection(); |
+} |
- // Video range start time is within the audio range. |
- if ((*video_ranges_itr).first >= (*audio_ranges_itr).first && |
- (*video_ranges_itr).first <= (*audio_ranges_itr).second) { |
- AddIntersectionRange(*video_ranges_itr, *audio_ranges_itr, |
- last_range_after_ended, ranges_out); |
- video_ranges_itr++; |
- success = true; |
- continue; |
- } |
+Ranges<TimeDelta> ChunkDemuxer::ComputeIntersection() const { |
+ lock_.AssertAcquired(); |
- // No overlap was found. Increment the earliest one and keep looking. |
- if ((*audio_ranges_itr).first < (*video_ranges_itr).first) |
- audio_ranges_itr++; |
- else |
- video_ranges_itr++; |
- } |
+ if (!audio_ || !video_) |
+ return Ranges<TimeDelta>(); |
- return success; |
-} |
- |
-bool ChunkDemuxer::CopyIntoRanges( |
- const SourceBufferStream::TimespanList& timespans, |
- Ranges* ranges_out) const { |
- for (SourceBufferStream::TimespanList::const_iterator itr = timespans.begin(); |
- itr != timespans.end(); ++itr) { |
- ranges_out->push_back(*itr); |
+ // Include ranges that have been buffered in both |audio_| and |video_|. |
+ Ranges<TimeDelta> audio_ranges = audio_->GetBufferedTime(); |
+ Ranges<TimeDelta> video_ranges = video_->GetBufferedTime(); |
+ Ranges<TimeDelta> result = audio_ranges.IntersectionWith(video_ranges); |
+ |
+ if (state_ == ENDED && result.size() > 0) { |
+ // If appending has ended, extend the last intersection range to include the |
+ // max end time of the last audio/video range. This allows the buffered |
+ // information to match the actual time range that will get played out if |
+ // the streams have slightly different lengths. |
+ TimeDelta audio_start = audio_ranges.start(audio_ranges.size() - 1); |
+ TimeDelta audio_end = audio_ranges.end(audio_ranges.size() - 1); |
+ TimeDelta video_start = video_ranges.start(video_ranges.size() - 1); |
+ TimeDelta video_end = video_ranges.end(video_ranges.size() - 1); |
+ |
+ // Verify the last audio range overlaps with the last video range. |
+ // This is enforced by the logic that controls the transition to ENDED. |
+ DCHECK((audio_start <= video_start && video_start <= audio_end) || |
+ (video_start <= audio_start && audio_start <= video_end)); |
+ result.Add(result.end(result.size()-1), std::max(audio_end, video_end)); |
} |
- return !timespans.empty(); |
-} |
- |
-void ChunkDemuxer::AddIntersectionRange( |
- SourceBufferStream::Timespan timespan_a, |
- SourceBufferStream::Timespan timespan_b, |
- bool last_range_after_ended, |
- Ranges* ranges_out) const { |
- base::TimeDelta start = timespan_a.first; |
- |
- // If this is the last range after EndOfStream() was called, choose the later |
- // end point of the ranges, otherwise choose the earlier. |
- base::TimeDelta end; |
- if (last_range_after_ended) |
- end = std::max(timespan_a.second, timespan_b.second); |
- else |
- end = std::min(timespan_a.second, timespan_b.second); |
- ranges_out->push_back(std::make_pair(start, end)); |
+ return result; |
} |
bool ChunkDemuxer::AppendData(const std::string& id, |
@@ -731,7 +695,7 @@ bool ChunkDemuxer::AppendData(const std::string& id, |
DCHECK(data); |
DCHECK_GT(length, 0u); |
- int64 buffered_bytes = 0; |
+ Ranges<TimeDelta> ranges; |
PipelineStatusCB cb; |
{ |
@@ -772,13 +736,26 @@ bool ChunkDemuxer::AppendData(const std::string& id, |
std::swap(cb, seek_cb_); |
} |
- buffered_bytes_ += length; |
- buffered_bytes = buffered_bytes_; |
+ if (duration_ > TimeDelta() && duration_ != kInfiniteDuration()) { |
+ if (audio_ && !video_) { |
+ ranges = audio_->GetBufferedTime(); |
+ } else if (!audio_ && video_) { |
+ ranges = video_->GetBufferedTime(); |
+ } else { |
+ ranges = ComputeIntersection(); |
+ } |
+ } |
} |
- // Notify the host of 'network activity' because we got data, using a bogus |
- // range. |
- host_->AddBufferedByteRange(0, buffered_bytes); |
+ DCHECK(!ranges.size() || duration_ > TimeDelta()); |
+ for (size_t i = 0; i < ranges.size(); ++i) { |
+ // Notify the host of 'network activity' because we got data. |
+ int64 start = |
+ kFakeTotalBytes * ranges.start(i).InSecondsF() / duration_.InSecondsF(); |
+ int64 end = |
+ kFakeTotalBytes * ranges.end(i).InSecondsF() / duration_.InSecondsF(); |
+ host_->AddBufferedByteRange(start, end); |
+ } |
if (!cb.is_null()) |
cb.Run(PIPELINE_OK); |
@@ -821,10 +798,10 @@ bool ChunkDemuxer::EndOfStream(PipelineStatus status) { |
if (video_) |
video_->EndOfStream(); |
- ChangeState_Locked(ENDED); |
- |
if (status != PIPELINE_OK) |
ReportError_Locked(status); |
+ else |
+ ChangeState_Locked(ENDED); |
return true; |
} |
@@ -928,8 +905,7 @@ bool ChunkDemuxer::CanEndOfStream_Locked() const { |
(!video_ || video_->CanEndOfStream()); |
} |
-void ChunkDemuxer::OnStreamParserInitDone(bool success, |
- base::TimeDelta duration) { |
+void ChunkDemuxer::OnStreamParserInitDone(bool success, TimeDelta duration) { |
DVLOG(1) << "OnSourceBufferInitDone(" << success << ", " |
<< duration.InSecondsF() << ")"; |
lock_.AssertAcquired(); |
@@ -947,6 +923,8 @@ void ChunkDemuxer::OnStreamParserInitDone(bool success, |
(!source_id_video_.empty() && !video_)) |
return; |
+ if (duration_ > TimeDelta() && duration_ != kInfiniteDuration()) |
+ host_->SetTotalBytes(kFakeTotalBytes); |
host_->SetDuration(duration_); |
ChangeState_Locked(INITIALIZED); |
@@ -1024,7 +1002,7 @@ bool ChunkDemuxer::OnNeedKey(scoped_array<uint8> init_data, |
} |
void ChunkDemuxer::OnNewMediaSegment(const std::string& source_id, |
- base::TimeDelta start_timestamp) { |
+ TimeDelta start_timestamp) { |
// TODO(vrk): There should be a special case for the first appends where all |
// streams (for both demuxed and muxed case) begin at the earliest stream |
// timestamp. (crbug.com/132815) |