Refactor WebsiteSettings to operate on a SecurityInfo
[chromium-blink-merge.git] / media / formats / mpeg / mpeg_audio_stream_parser_base.cc
blob231fea45a78f997bcb389f42198a2cb6646a7c3c
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/mpeg/mpeg_audio_stream_parser_base.h"
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/message_loop/message_loop.h"
10 #include "media/base/stream_parser_buffer.h"
11 #include "media/base/text_track_config.h"
12 #include "media/base/timestamp_constants.h"
13 #include "media/base/video_decoder_config.h"
15 namespace media {
17 static const uint32 kICYStartCode = 0x49435920; // 'ICY '
19 // Arbitrary upper bound on the size of an IceCast header before it
20 // triggers an error.
21 static const int kMaxIcecastHeaderSize = 4096;
23 static const uint32 kID3StartCodeMask = 0xffffff00;
24 static const uint32 kID3v1StartCode = 0x54414700; // 'TAG\0'
25 static const int kID3v1Size = 128;
26 static const int kID3v1ExtendedSize = 227;
27 static const uint32 kID3v2StartCode = 0x49443300; // 'ID3\0'
29 static int LocateEndOfHeaders(const uint8_t* buf, int buf_len, int i) {
30 bool was_lf = false;
31 char last_c = '\0';
32 for (; i < buf_len; ++i) {
33 char c = buf[i];
34 if (c == '\n') {
35 if (was_lf)
36 return i + 1;
37 was_lf = true;
38 } else if (c != '\r' || last_c != '\n') {
39 was_lf = false;
41 last_c = c;
43 return -1;
46 MPEGAudioStreamParserBase::MPEGAudioStreamParserBase(uint32 start_code_mask,
47 AudioCodec audio_codec,
48 int codec_delay)
49 : state_(UNINITIALIZED),
50 in_media_segment_(false),
51 start_code_mask_(start_code_mask),
52 audio_codec_(audio_codec),
53 codec_delay_(codec_delay) {}
55 MPEGAudioStreamParserBase::~MPEGAudioStreamParserBase() {}
57 void MPEGAudioStreamParserBase::Init(
58 const InitCB& init_cb,
59 const NewConfigCB& config_cb,
60 const NewBuffersCB& new_buffers_cb,
61 bool ignore_text_tracks,
62 const EncryptedMediaInitDataCB& encrypted_media_init_data_cb,
63 const NewMediaSegmentCB& new_segment_cb,
64 const base::Closure& end_of_segment_cb,
65 const scoped_refptr<MediaLog>& media_log) {
66 DVLOG(1) << __FUNCTION__;
67 DCHECK_EQ(state_, UNINITIALIZED);
68 init_cb_ = init_cb;
69 config_cb_ = config_cb;
70 new_buffers_cb_ = new_buffers_cb;
71 new_segment_cb_ = new_segment_cb;
72 end_of_segment_cb_ = end_of_segment_cb;
73 media_log_ = media_log;
75 ChangeState(INITIALIZED);
78 void MPEGAudioStreamParserBase::Flush() {
79 DVLOG(1) << __FUNCTION__;
80 DCHECK_NE(state_, UNINITIALIZED);
81 queue_.Reset();
82 timestamp_helper_->SetBaseTimestamp(base::TimeDelta());
83 in_media_segment_ = false;
86 bool MPEGAudioStreamParserBase::Parse(const uint8* buf, int size) {
87 DVLOG(1) << __FUNCTION__ << "(" << size << ")";
88 DCHECK(buf);
89 DCHECK_GT(size, 0);
90 DCHECK_NE(state_, UNINITIALIZED);
92 if (state_ == PARSE_ERROR)
93 return false;
95 DCHECK_EQ(state_, INITIALIZED);
97 queue_.Push(buf, size);
99 bool end_of_segment = true;
100 BufferQueue buffers;
101 for (;;) {
102 const uint8* data;
103 int data_size;
104 queue_.Peek(&data, &data_size);
106 if (data_size < 4)
107 break;
109 uint32 start_code = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
110 int bytes_read = 0;
111 bool parsed_metadata = true;
112 if ((start_code & start_code_mask_) == start_code_mask_) {
113 bytes_read = ParseFrame(data, data_size, &buffers);
115 // Only allow the current segment to end if a full frame has been parsed.
116 end_of_segment = bytes_read > 0;
117 parsed_metadata = false;
118 } else if (start_code == kICYStartCode) {
119 bytes_read = ParseIcecastHeader(data, data_size);
120 } else if ((start_code & kID3StartCodeMask) == kID3v1StartCode) {
121 bytes_read = ParseID3v1(data, data_size);
122 } else if ((start_code & kID3StartCodeMask) == kID3v2StartCode) {
123 bytes_read = ParseID3v2(data, data_size);
124 } else {
125 bytes_read = FindNextValidStartCode(data, data_size);
127 if (bytes_read > 0) {
128 DVLOG(1) << "Unexpected start code 0x" << std::hex << start_code;
129 DVLOG(1) << "SKIPPING " << bytes_read << " bytes of garbage.";
133 CHECK_LE(bytes_read, data_size);
135 if (bytes_read < 0) {
136 ChangeState(PARSE_ERROR);
137 return false;
138 } else if (bytes_read == 0) {
139 // Need more data.
140 break;
143 // Send pending buffers if we have encountered metadata.
144 if (parsed_metadata && !buffers.empty() && !SendBuffers(&buffers, true))
145 return false;
147 queue_.Pop(bytes_read);
148 end_of_segment = true;
151 if (buffers.empty())
152 return true;
154 // Send buffers collected in this append that haven't been sent yet.
155 return SendBuffers(&buffers, end_of_segment);
158 void MPEGAudioStreamParserBase::ChangeState(State state) {
159 DVLOG(1) << __FUNCTION__ << "() : " << state_ << " -> " << state;
160 state_ = state;
163 int MPEGAudioStreamParserBase::ParseFrame(const uint8* data,
164 int size,
165 BufferQueue* buffers) {
166 DVLOG(2) << __FUNCTION__ << "(" << size << ")";
168 int sample_rate;
169 ChannelLayout channel_layout;
170 int frame_size;
171 int sample_count;
172 bool metadata_frame = false;
173 int bytes_read = ParseFrameHeader(data,
174 size,
175 &frame_size,
176 &sample_rate,
177 &channel_layout,
178 &sample_count,
179 &metadata_frame);
181 if (bytes_read <= 0)
182 return bytes_read;
184 // Make sure data contains the entire frame.
185 if (size < frame_size)
186 return 0;
188 DVLOG(2) << " sample_rate " << sample_rate
189 << " channel_layout " << channel_layout
190 << " frame_size " << frame_size
191 << " sample_count " << sample_count;
193 if (config_.IsValidConfig() &&
194 (config_.samples_per_second() != sample_rate ||
195 config_.channel_layout() != channel_layout)) {
196 // Clear config data so that a config change is initiated.
197 config_ = AudioDecoderConfig();
199 // Send all buffers associated with the previous config.
200 if (!buffers->empty() && !SendBuffers(buffers, true))
201 return -1;
204 if (!config_.IsValidConfig()) {
205 config_.Initialize(audio_codec_,
206 kSampleFormatF32,
207 channel_layout,
208 sample_rate,
209 NULL,
211 false,
212 base::TimeDelta(),
213 codec_delay_);
215 base::TimeDelta base_timestamp;
216 if (timestamp_helper_)
217 base_timestamp = timestamp_helper_->GetTimestamp();
219 timestamp_helper_.reset(new AudioTimestampHelper(sample_rate));
220 timestamp_helper_->SetBaseTimestamp(base_timestamp);
222 VideoDecoderConfig video_config;
223 if (!config_cb_.Run(config_, video_config, TextTrackConfigMap()))
224 return -1;
226 if (!init_cb_.is_null()) {
227 InitParameters params(kInfiniteDuration());
228 params.auto_update_timestamp_offset = true;
229 base::ResetAndReturn(&init_cb_).Run(params);
233 if (metadata_frame)
234 return frame_size;
236 // TODO(wolenetz/acolwell): Validate and use a common cross-parser TrackId
237 // type and allow multiple audio tracks, if applicable. See
238 // https://crbug.com/341581.
239 scoped_refptr<StreamParserBuffer> buffer =
240 StreamParserBuffer::CopyFrom(data, frame_size, true,
241 DemuxerStream::AUDIO, 0);
242 buffer->set_timestamp(timestamp_helper_->GetTimestamp());
243 buffer->set_duration(timestamp_helper_->GetFrameDuration(sample_count));
244 buffers->push_back(buffer);
246 timestamp_helper_->AddFrames(sample_count);
248 return frame_size;
251 int MPEGAudioStreamParserBase::ParseIcecastHeader(const uint8* data, int size) {
252 DVLOG(1) << __FUNCTION__ << "(" << size << ")";
254 if (size < 4)
255 return 0;
257 if (memcmp("ICY ", data, 4))
258 return -1;
260 int locate_size = std::min(size, kMaxIcecastHeaderSize);
261 int offset = LocateEndOfHeaders(data, locate_size, 4);
262 if (offset < 0) {
263 if (locate_size == kMaxIcecastHeaderSize) {
264 MEDIA_LOG(ERROR, media_log_) << "Icecast header is too large.";
265 return -1;
268 return 0;
271 return offset;
274 int MPEGAudioStreamParserBase::ParseID3v1(const uint8* data, int size) {
275 DVLOG(1) << __FUNCTION__ << "(" << size << ")";
277 if (size < kID3v1Size)
278 return 0;
280 // TODO(acolwell): Add code to actually validate ID3v1 data and
281 // expose it as a metadata text track.
282 return !memcmp(data, "TAG+", 4) ? kID3v1ExtendedSize : kID3v1Size;
285 int MPEGAudioStreamParserBase::ParseID3v2(const uint8* data, int size) {
286 DVLOG(1) << __FUNCTION__ << "(" << size << ")";
288 if (size < 10)
289 return 0;
291 BitReader reader(data, size);
292 int32 id;
293 int version;
294 uint8 flags;
295 int32 id3_size;
297 if (!reader.ReadBits(24, &id) ||
298 !reader.ReadBits(16, &version) ||
299 !reader.ReadBits(8, &flags) ||
300 !ParseSyncSafeInt(&reader, &id3_size)) {
301 return -1;
304 int32 actual_tag_size = 10 + id3_size;
306 // Increment size if 'Footer present' flag is set.
307 if (flags & 0x10)
308 actual_tag_size += 10;
310 // Make sure we have the entire tag.
311 if (size < actual_tag_size)
312 return 0;
314 // TODO(acolwell): Add code to actually validate ID3v2 data and
315 // expose it as a metadata text track.
316 return actual_tag_size;
319 bool MPEGAudioStreamParserBase::ParseSyncSafeInt(BitReader* reader,
320 int32* value) {
321 *value = 0;
322 for (int i = 0; i < 4; ++i) {
323 uint8 tmp;
324 if (!reader->ReadBits(1, &tmp) || tmp != 0) {
325 MEDIA_LOG(ERROR, media_log_) << "ID3 syncsafe integer byte MSb is not 0!";
326 return false;
329 if (!reader->ReadBits(7, &tmp))
330 return false;
332 *value <<= 7;
333 *value += tmp;
336 return true;
339 int MPEGAudioStreamParserBase::FindNextValidStartCode(const uint8* data,
340 int size) const {
341 const uint8* start = data;
342 const uint8* end = data + size;
344 while (start < end) {
345 int bytes_left = end - start;
346 const uint8* candidate_start_code =
347 static_cast<const uint8*>(memchr(start, 0xff, bytes_left));
349 if (!candidate_start_code)
350 return 0;
352 bool parse_header_failed = false;
353 const uint8* sync = candidate_start_code;
354 // Try to find 3 valid frames in a row. 3 was selected to decrease
355 // the probability of false positives.
356 for (int i = 0; i < 3; ++i) {
357 int sync_size = end - sync;
358 int frame_size;
359 int sync_bytes = ParseFrameHeader(
360 sync, sync_size, &frame_size, NULL, NULL, NULL, NULL);
362 if (sync_bytes == 0)
363 return 0;
365 if (sync_bytes > 0) {
366 DCHECK_LT(sync_bytes, sync_size);
368 // Skip over this frame so we can check the next one.
369 sync += frame_size;
371 // Make sure the next frame starts inside the buffer.
372 if (sync >= end)
373 return 0;
374 } else {
375 DVLOG(1) << "ParseFrameHeader() " << i << " failed @" << (sync - data);
376 parse_header_failed = true;
377 break;
381 if (parse_header_failed) {
382 // One of the frame header parses failed so |candidate_start_code|
383 // did not point to the start of a real frame. Move |start| forward
384 // so we can find the next candidate.
385 start = candidate_start_code + 1;
386 continue;
389 return candidate_start_code - data;
392 return 0;
395 bool MPEGAudioStreamParserBase::SendBuffers(BufferQueue* buffers,
396 bool end_of_segment) {
397 DCHECK(!buffers->empty());
399 if (!in_media_segment_) {
400 in_media_segment_ = true;
401 new_segment_cb_.Run();
404 BufferQueue empty_video_buffers;
405 TextBufferQueueMap empty_text_map;
406 if (!new_buffers_cb_.Run(*buffers, empty_video_buffers, empty_text_map))
407 return false;
408 buffers->clear();
410 if (end_of_segment) {
411 in_media_segment_ = false;
412 end_of_segment_cb_.Run();
415 timestamp_helper_->SetBaseTimestamp(base::TimeDelta());
416 return true;
419 } // namespace media