Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / media / formats / webm / webm_stream_parser.cc
blobb978b96b4d64ed957e40655cdc5222ccd829aa16
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/formats/webm/webm_stream_parser.h"
7 #include <string>
9 #include "base/callback.h"
10 #include "base/callback_helpers.h"
11 #include "base/logging.h"
12 #include "media/base/timestamp_constants.h"
13 #include "media/formats/webm/webm_cluster_parser.h"
14 #include "media/formats/webm/webm_constants.h"
15 #include "media/formats/webm/webm_content_encodings.h"
16 #include "media/formats/webm/webm_info_parser.h"
17 #include "media/formats/webm/webm_tracks_parser.h"
19 namespace media {
21 WebMStreamParser::WebMStreamParser()
22 : state_(kWaitingForInit),
23 unknown_segment_size_(false) {
26 WebMStreamParser::~WebMStreamParser() {
29 void WebMStreamParser::Init(
30 const InitCB& init_cb,
31 const NewConfigCB& config_cb,
32 const NewBuffersCB& new_buffers_cb,
33 bool ignore_text_tracks,
34 const EncryptedMediaInitDataCB& encrypted_media_init_data_cb,
35 const NewMediaSegmentCB& new_segment_cb,
36 const base::Closure& end_of_segment_cb,
37 const scoped_refptr<MediaLog>& media_log) {
38 DCHECK_EQ(state_, kWaitingForInit);
39 DCHECK(init_cb_.is_null());
40 DCHECK(!init_cb.is_null());
41 DCHECK(!config_cb.is_null());
42 DCHECK(!new_buffers_cb.is_null());
43 DCHECK(!encrypted_media_init_data_cb.is_null());
44 DCHECK(!new_segment_cb.is_null());
45 DCHECK(!end_of_segment_cb.is_null());
47 ChangeState(kParsingHeaders);
48 init_cb_ = init_cb;
49 config_cb_ = config_cb;
50 new_buffers_cb_ = new_buffers_cb;
51 ignore_text_tracks_ = ignore_text_tracks;
52 encrypted_media_init_data_cb_ = encrypted_media_init_data_cb;
53 new_segment_cb_ = new_segment_cb;
54 end_of_segment_cb_ = end_of_segment_cb;
55 media_log_ = media_log;
58 void WebMStreamParser::Flush() {
59 DCHECK_NE(state_, kWaitingForInit);
61 byte_queue_.Reset();
62 if (cluster_parser_)
63 cluster_parser_->Reset();
64 if (state_ == kParsingClusters) {
65 ChangeState(kParsingHeaders);
66 end_of_segment_cb_.Run();
70 bool WebMStreamParser::Parse(const uint8* buf, int size) {
71 DCHECK_NE(state_, kWaitingForInit);
73 if (state_ == kError)
74 return false;
76 byte_queue_.Push(buf, size);
78 int result = 0;
79 int bytes_parsed = 0;
80 const uint8* cur = NULL;
81 int cur_size = 0;
83 byte_queue_.Peek(&cur, &cur_size);
84 while (cur_size > 0) {
85 State oldState = state_;
86 switch (state_) {
87 case kParsingHeaders:
88 result = ParseInfoAndTracks(cur, cur_size);
89 break;
91 case kParsingClusters:
92 result = ParseCluster(cur, cur_size);
93 break;
95 case kWaitingForInit:
96 case kError:
97 return false;
100 if (result < 0) {
101 ChangeState(kError);
102 return false;
105 if (state_ == oldState && result == 0)
106 break;
108 DCHECK_GE(result, 0);
109 cur += result;
110 cur_size -= result;
111 bytes_parsed += result;
114 byte_queue_.Pop(bytes_parsed);
115 return true;
118 void WebMStreamParser::ChangeState(State new_state) {
119 DVLOG(1) << "ChangeState() : " << state_ << " -> " << new_state;
120 state_ = new_state;
123 int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) {
124 DVLOG(2) << "ParseInfoAndTracks()";
125 DCHECK(data);
126 DCHECK_GT(size, 0);
128 const uint8* cur = data;
129 int cur_size = size;
130 int bytes_parsed = 0;
132 int id;
133 int64 element_size;
134 int result = WebMParseElementHeader(cur, cur_size, &id, &element_size);
136 if (result <= 0)
137 return result;
139 switch (id) {
140 case kWebMIdEBMLHeader:
141 case kWebMIdSeekHead:
142 case kWebMIdVoid:
143 case kWebMIdCRC32:
144 case kWebMIdCues:
145 case kWebMIdChapters:
146 case kWebMIdTags:
147 case kWebMIdAttachments:
148 // TODO(matthewjheaney): Implement support for chapters.
149 if (cur_size < (result + element_size)) {
150 // We don't have the whole element yet. Signal we need more data.
151 return 0;
153 // Skip the element.
154 return result + element_size;
155 break;
156 case kWebMIdCluster:
157 if (!cluster_parser_) {
158 MEDIA_LOG(ERROR, media_log_) << "Found Cluster element before Info.";
159 return -1;
161 ChangeState(kParsingClusters);
162 new_segment_cb_.Run();
163 return 0;
164 break;
165 case kWebMIdSegment:
166 // Segment of unknown size indicates live stream.
167 if (element_size == kWebMUnknownSize)
168 unknown_segment_size_ = true;
169 // Just consume the segment header.
170 return result;
171 break;
172 case kWebMIdInfo:
173 // We've found the element we are looking for.
174 break;
175 default: {
176 MEDIA_LOG(ERROR, media_log_) << "Unexpected element ID 0x" << std::hex
177 << id;
178 return -1;
182 WebMInfoParser info_parser;
183 result = info_parser.Parse(cur, cur_size);
185 if (result <= 0)
186 return result;
188 cur += result;
189 cur_size -= result;
190 bytes_parsed += result;
192 WebMTracksParser tracks_parser(media_log_, ignore_text_tracks_);
193 result = tracks_parser.Parse(cur, cur_size);
195 if (result <= 0)
196 return result;
198 bytes_parsed += result;
200 double timecode_scale_in_us = info_parser.timecode_scale() / 1000.0;
201 InitParameters params(kInfiniteDuration());
203 if (info_parser.duration() > 0) {
204 int64 duration_in_us = info_parser.duration() * timecode_scale_in_us;
205 params.duration = base::TimeDelta::FromMicroseconds(duration_in_us);
208 params.timeline_offset = info_parser.date_utc();
210 if (unknown_segment_size_ && (info_parser.duration() <= 0) &&
211 !info_parser.date_utc().is_null()) {
212 params.liveness = DemuxerStream::LIVENESS_LIVE;
213 } else if (info_parser.duration() >= 0) {
214 params.liveness = DemuxerStream::LIVENESS_RECORDED;
215 } else {
216 params.liveness = DemuxerStream::LIVENESS_UNKNOWN;
219 const AudioDecoderConfig& audio_config = tracks_parser.audio_decoder_config();
220 if (audio_config.is_encrypted())
221 OnEncryptedMediaInitData(tracks_parser.audio_encryption_key_id());
223 const VideoDecoderConfig& video_config = tracks_parser.video_decoder_config();
224 if (video_config.is_encrypted())
225 OnEncryptedMediaInitData(tracks_parser.video_encryption_key_id());
227 if (!config_cb_.Run(audio_config,
228 video_config,
229 tracks_parser.text_tracks())) {
230 DVLOG(1) << "New config data isn't allowed.";
231 return -1;
234 cluster_parser_.reset(new WebMClusterParser(
235 info_parser.timecode_scale(), tracks_parser.audio_track_num(),
236 tracks_parser.GetAudioDefaultDuration(timecode_scale_in_us),
237 tracks_parser.video_track_num(),
238 tracks_parser.GetVideoDefaultDuration(timecode_scale_in_us),
239 tracks_parser.text_tracks(), tracks_parser.ignored_tracks(),
240 tracks_parser.audio_encryption_key_id(),
241 tracks_parser.video_encryption_key_id(), audio_config.codec(),
242 media_log_));
244 if (!init_cb_.is_null())
245 base::ResetAndReturn(&init_cb_).Run(params);
247 return bytes_parsed;
250 int WebMStreamParser::ParseCluster(const uint8* data, int size) {
251 if (!cluster_parser_)
252 return -1;
254 int bytes_parsed = cluster_parser_->Parse(data, size);
255 if (bytes_parsed < 0)
256 return bytes_parsed;
258 const BufferQueue& audio_buffers = cluster_parser_->GetAudioBuffers();
259 const BufferQueue& video_buffers = cluster_parser_->GetVideoBuffers();
260 const TextBufferQueueMap& text_map = cluster_parser_->GetTextBuffers();
262 bool cluster_ended = cluster_parser_->cluster_ended();
264 if ((!audio_buffers.empty() || !video_buffers.empty() ||
265 !text_map.empty()) &&
266 !new_buffers_cb_.Run(audio_buffers, video_buffers, text_map)) {
267 return -1;
270 if (cluster_ended) {
271 ChangeState(kParsingHeaders);
272 end_of_segment_cb_.Run();
275 return bytes_parsed;
278 void WebMStreamParser::OnEncryptedMediaInitData(const std::string& key_id) {
279 std::vector<uint8> key_id_vector(key_id.begin(), key_id.end());
280 encrypted_media_init_data_cb_.Run(EmeInitDataType::WEBM, key_id_vector);
283 } // namespace media