Commit 44a866db authored by sergeyu@chromium.org's avatar sergeyu@chromium.org

Add live mode detection in WebM MediaSource parser.

The live mode is indicated by presense of DateUTC element and unknown
segment duration and size.

BUG=338529

Review URL: https://codereview.chromium.org/231283005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@266452 0039d316-1c4b-4281-b951-d872f2087c98
parent 7f45a688
...@@ -44,6 +44,12 @@ class MEDIA_EXPORT DemuxerHost { ...@@ -44,6 +44,12 @@ class MEDIA_EXPORT DemuxerHost {
class MEDIA_EXPORT Demuxer { class MEDIA_EXPORT Demuxer {
public: public:
enum Liveness {
LIVENESS_UNKNOWN,
LIVENESS_RECORDED,
LIVENESS_LIVE,
};
// A new potentially encrypted stream has been parsed. // A new potentially encrypted stream has been parsed.
// First parameter - The type of initialization data. // First parameter - The type of initialization data.
// Second parameter - The initialization data associated with the stream. // Second parameter - The initialization data associated with the stream.
...@@ -92,6 +98,9 @@ class MEDIA_EXPORT Demuxer { ...@@ -92,6 +98,9 @@ class MEDIA_EXPORT Demuxer {
// a null Time is returned. // a null Time is returned.
virtual base::Time GetTimelineOffset() const = 0; virtual base::Time GetTimelineOffset() const = 0;
// Returns liveness of the stream, i.e. whether it is recorded or live.
virtual Liveness GetLiveness() const = 0;
private: private:
DISALLOW_COPY_AND_ASSIGN(Demuxer); DISALLOW_COPY_AND_ASSIGN(Demuxer);
}; };
......
...@@ -40,6 +40,7 @@ class MockDemuxer : public Demuxer { ...@@ -40,6 +40,7 @@ class MockDemuxer : public Demuxer {
MOCK_METHOD1(GetStream, DemuxerStream*(DemuxerStream::Type)); MOCK_METHOD1(GetStream, DemuxerStream*(DemuxerStream::Type));
MOCK_CONST_METHOD0(GetStartTime, base::TimeDelta()); MOCK_CONST_METHOD0(GetStartTime, base::TimeDelta());
MOCK_CONST_METHOD0(GetTimelineOffset, base::Time()); MOCK_CONST_METHOD0(GetTimelineOffset, base::Time());
MOCK_CONST_METHOD0(GetLiveness, Liveness());
private: private:
DISALLOW_COPY_AND_ASSIGN(MockDemuxer); DISALLOW_COPY_AND_ASSIGN(MockDemuxer);
......
...@@ -11,7 +11,8 @@ namespace media { ...@@ -11,7 +11,8 @@ namespace media {
StreamParser::InitParameters::InitParameters(base::TimeDelta duration) StreamParser::InitParameters::InitParameters(base::TimeDelta duration)
: duration(duration), : duration(duration),
auto_update_timestamp_offset(false) { auto_update_timestamp_offset(false),
liveness(Demuxer::LIVENESS_UNKNOWN) {
} }
StreamParser::StreamParser() {} StreamParser::StreamParser() {}
......
...@@ -60,6 +60,9 @@ class MEDIA_EXPORT StreamParser { ...@@ -60,6 +60,9 @@ class MEDIA_EXPORT StreamParser {
// Indicates that timestampOffset should be updated based on the earliest // Indicates that timestampOffset should be updated based on the earliest
// end timestamp (audio or video) provided during each NewBuffersCB. // end timestamp (audio or video) provided during each NewBuffersCB.
bool auto_update_timestamp_offset; bool auto_update_timestamp_offset;
// Indicates live stream.
Demuxer::Liveness liveness;
}; };
// Indicates completion of parser initialization. // Indicates completion of parser initialization.
......
...@@ -974,6 +974,7 @@ ChunkDemuxer::ChunkDemuxer(const base::Closure& open_cb, ...@@ -974,6 +974,7 @@ ChunkDemuxer::ChunkDemuxer(const base::Closure& open_cb,
log_cb_(log_cb), log_cb_(log_cb),
duration_(kNoTimestamp()), duration_(kNoTimestamp()),
user_specified_duration_(-1), user_specified_duration_(-1),
liveness_(LIVENESS_UNKNOWN),
splice_frames_enabled_(splice_frames_enabled) { splice_frames_enabled_(splice_frames_enabled) {
DCHECK(!open_cb_.is_null()); DCHECK(!open_cb_.is_null());
DCHECK(!need_key_cb_.is_null()); DCHECK(!need_key_cb_.is_null());
...@@ -1064,6 +1065,10 @@ base::Time ChunkDemuxer::GetTimelineOffset() const { ...@@ -1064,6 +1065,10 @@ base::Time ChunkDemuxer::GetTimelineOffset() const {
return timeline_offset_; return timeline_offset_;
} }
Demuxer::Liveness ChunkDemuxer::GetLiveness() const {
return liveness_;
}
void ChunkDemuxer::StartWaitingForSeek(TimeDelta seek_time) { void ChunkDemuxer::StartWaitingForSeek(TimeDelta seek_time) {
DVLOG(1) << "StartWaitingForSeek()"; DVLOG(1) << "StartWaitingForSeek()";
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
...@@ -1508,6 +1513,17 @@ void ChunkDemuxer::OnSourceInitDone( ...@@ -1508,6 +1513,17 @@ void ChunkDemuxer::OnSourceInitDone(
timeline_offset_ = params.timeline_offset; timeline_offset_ = params.timeline_offset;
} }
if (params.liveness != LIVENESS_UNKNOWN) {
if (liveness_ != LIVENESS_UNKNOWN && params.liveness != liveness_) {
MEDIA_LOG(log_cb_)
<< "Liveness is not the same across all SourceBuffers.";
ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN);
return;
}
liveness_ = params.liveness;
}
// Wait until all streams have initialized. // Wait until all streams have initialized.
if ((!source_id_audio_.empty() && !audio_) || if ((!source_id_audio_.empty() && !audio_) ||
(!source_id_video_.empty() && !video_)) { (!source_id_video_.empty() && !video_)) {
......
...@@ -159,6 +159,7 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { ...@@ -159,6 +159,7 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
virtual DemuxerStream* GetStream(DemuxerStream::Type type) OVERRIDE; virtual DemuxerStream* GetStream(DemuxerStream::Type type) OVERRIDE;
virtual base::TimeDelta GetStartTime() const OVERRIDE; virtual base::TimeDelta GetStartTime() const OVERRIDE;
virtual base::Time GetTimelineOffset() const OVERRIDE; virtual base::Time GetTimelineOffset() const OVERRIDE;
virtual Liveness GetLiveness() const OVERRIDE;
// Methods used by an external object to control this demuxer. // Methods used by an external object to control this demuxer.
// //
...@@ -366,6 +367,7 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer { ...@@ -366,6 +367,7 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
double user_specified_duration_; double user_specified_duration_;
base::Time timeline_offset_; base::Time timeline_offset_;
Liveness liveness_;
typedef std::map<std::string, SourceState*> SourceStateMap; typedef std::map<std::string, SourceState*> SourceStateMap;
SourceStateMap source_state_map_; SourceStateMap source_state_map_;
......
...@@ -394,6 +394,7 @@ FFmpegDemuxer::FFmpegDemuxer( ...@@ -394,6 +394,7 @@ FFmpegDemuxer::FFmpegDemuxer(
media_log_(media_log), media_log_(media_log),
bitrate_(0), bitrate_(0),
start_time_(kNoTimestamp()), start_time_(kNoTimestamp()),
liveness_(LIVENESS_UNKNOWN),
audio_disabled_(false), audio_disabled_(false),
text_enabled_(false), text_enabled_(false),
duration_known_(false), duration_known_(false),
...@@ -506,6 +507,11 @@ base::Time FFmpegDemuxer::GetTimelineOffset() const { ...@@ -506,6 +507,11 @@ base::Time FFmpegDemuxer::GetTimelineOffset() const {
return timeline_offset_; return timeline_offset_;
} }
Demuxer::Liveness FFmpegDemuxer::GetLiveness() const {
DCHECK(task_runner_->BelongsToCurrentThread());
return liveness_;
}
void FFmpegDemuxer::AddTextStreams() { void FFmpegDemuxer::AddTextStreams() {
DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(task_runner_->BelongsToCurrentThread());
...@@ -696,6 +702,14 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, ...@@ -696,6 +702,14 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
timeline_offset_ = ExtractTimelineOffset(format_context); timeline_offset_ = ExtractTimelineOffset(format_context);
if (max_duration == kInfiniteDuration() && !timeline_offset_.is_null()) {
liveness_ = LIVENESS_LIVE;
} else if (max_duration != kInfiniteDuration()) {
liveness_ = LIVENESS_RECORDED;
} else {
liveness_ = LIVENESS_UNKNOWN;
}
// Good to go: set the duration and bitrate and notify we're done // Good to go: set the duration and bitrate and notify we're done
// initializing. // initializing.
host_->SetDuration(max_duration); host_->SetDuration(max_duration);
......
...@@ -155,6 +155,7 @@ class MEDIA_EXPORT FFmpegDemuxer : public Demuxer { ...@@ -155,6 +155,7 @@ class MEDIA_EXPORT FFmpegDemuxer : public Demuxer {
virtual DemuxerStream* GetStream(DemuxerStream::Type type) OVERRIDE; virtual DemuxerStream* GetStream(DemuxerStream::Type type) OVERRIDE;
virtual base::TimeDelta GetStartTime() const OVERRIDE; virtual base::TimeDelta GetStartTime() const OVERRIDE;
virtual base::Time GetTimelineOffset() const OVERRIDE; virtual base::Time GetTimelineOffset() const OVERRIDE;
virtual Liveness GetLiveness() const OVERRIDE;
// Calls |need_key_cb_| with the initialization data encountered in the file. // Calls |need_key_cb_| with the initialization data encountered in the file.
void FireNeedKey(const std::string& init_data_type, void FireNeedKey(const std::string& init_data_type,
...@@ -251,6 +252,9 @@ class MEDIA_EXPORT FFmpegDemuxer : public Demuxer { ...@@ -251,6 +252,9 @@ class MEDIA_EXPORT FFmpegDemuxer : public Demuxer {
// time if the file doesn't have an association to Time. // time if the file doesn't have an association to Time.
base::Time timeline_offset_; base::Time timeline_offset_;
// Liveness of the stream.
Liveness liveness_;
// Whether audio has been disabled for this demuxer (in which case this class // Whether audio has been disabled for this demuxer (in which case this class
// drops packets destined for AUDIO demuxer streams on the floor). // drops packets destined for AUDIO demuxer streams on the floor).
bool audio_disabled_; bool audio_disabled_;
......
...@@ -20,6 +20,7 @@ namespace media { ...@@ -20,6 +20,7 @@ namespace media {
WebMStreamParser::WebMStreamParser() WebMStreamParser::WebMStreamParser()
: state_(kWaitingForInit), : state_(kWaitingForInit),
unknown_segment_size_(false),
parsing_cluster_(false) { parsing_cluster_(false) {
} }
...@@ -150,6 +151,9 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) { ...@@ -150,6 +151,9 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) {
return result + element_size; return result + element_size;
break; break;
case kWebMIdSegment: case kWebMIdSegment:
// Segment of unknown size indicates live stream.
if (element_size == kWebMUnknownSize)
unknown_segment_size_ = true;
// Just consume the segment header. // Just consume the segment header.
return result; return result;
break; break;
...@@ -190,6 +194,15 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) { ...@@ -190,6 +194,15 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) {
params.timeline_offset = info_parser.date_utc(); params.timeline_offset = info_parser.date_utc();
if (unknown_segment_size_ && (info_parser.duration() <= 0) &&
!info_parser.date_utc().is_null()) {
params.liveness = Demuxer::LIVENESS_LIVE;
} else if (info_parser.duration() >= 0) {
params.liveness = Demuxer::LIVENESS_RECORDED;
} else {
params.liveness = Demuxer::LIVENESS_UNKNOWN;
}
const AudioDecoderConfig& audio_config = tracks_parser.audio_decoder_config(); const AudioDecoderConfig& audio_config = tracks_parser.audio_decoder_config();
if (audio_config.is_encrypted()) if (audio_config.is_encrypted())
FireNeedKey(tracks_parser.audio_encryption_key_id()); FireNeedKey(tracks_parser.audio_encryption_key_id());
...@@ -205,7 +218,6 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) { ...@@ -205,7 +218,6 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) {
return -1; return -1;
} }
cluster_parser_.reset(new WebMClusterParser( cluster_parser_.reset(new WebMClusterParser(
info_parser.timecode_scale(), info_parser.timecode_scale(),
tracks_parser.audio_track_num(), tracks_parser.audio_track_num(),
......
...@@ -76,6 +76,8 @@ class WebMStreamParser : public StreamParser { ...@@ -76,6 +76,8 @@ class WebMStreamParser : public StreamParser {
base::Closure end_of_segment_cb_; base::Closure end_of_segment_cb_;
LogCB log_cb_; LogCB log_cb_;
bool unknown_segment_size_;
// True if a new cluster id has been seen and its end has not yet been parsed. // True if a new cluster id has been seen and its end has not yet been parsed.
bool parsing_cluster_; bool parsing_cluster_;
......
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