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/mp2t/es_parser_mpeg1audio.h"
7 #include "base/basictypes.h"
9 #include "base/logging.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "media/base/audio_timestamp_helper.h"
12 #include "media/base/bit_reader.h"
13 #include "media/base/channel_layout.h"
14 #include "media/base/stream_parser_buffer.h"
15 #include "media/base/timestamp_constants.h"
16 #include "media/formats/common/offset_byte_queue.h"
17 #include "media/formats/mp2t/mp2t_common.h"
18 #include "media/formats/mpeg/mpeg1_audio_stream_parser.h"
23 struct EsParserMpeg1Audio::Mpeg1AudioFrame
{
24 // Pointer to the ES data.
30 // Number of samples in the frame.
33 // Frame offset in the ES queue.
37 EsParserMpeg1Audio::EsParserMpeg1Audio(
38 const NewAudioConfigCB
& new_audio_config_cb
,
39 const EmitBufferCB
& emit_buffer_cb
,
40 const scoped_refptr
<MediaLog
>& media_log
)
41 : media_log_(media_log
),
42 new_audio_config_cb_(new_audio_config_cb
),
43 emit_buffer_cb_(emit_buffer_cb
) {
46 EsParserMpeg1Audio::~EsParserMpeg1Audio() {
49 bool EsParserMpeg1Audio::ParseFromEsQueue() {
50 // Look for every MPEG1 audio frame in the ES buffer.
51 Mpeg1AudioFrame mpeg1audio_frame
;
52 while (LookForMpeg1AudioFrame(&mpeg1audio_frame
)) {
53 // Update the audio configuration if needed.
54 DCHECK_GE(mpeg1audio_frame
.size
, MPEG1AudioStreamParser::kHeaderSize
);
55 if (!UpdateAudioConfiguration(mpeg1audio_frame
.data
))
58 // Get the PTS & the duration of this access unit.
59 TimingDesc current_timing_desc
=
60 GetTimingDescriptor(mpeg1audio_frame
.queue_offset
);
61 if (current_timing_desc
.pts
!= kNoTimestamp())
62 audio_timestamp_helper_
->SetBaseTimestamp(current_timing_desc
.pts
);
64 if (audio_timestamp_helper_
->base_timestamp() == kNoTimestamp()) {
65 DVLOG(1) << "Skipping audio frame with unknown timestamp";
66 SkipMpeg1AudioFrame(mpeg1audio_frame
);
69 base::TimeDelta current_pts
= audio_timestamp_helper_
->GetTimestamp();
70 base::TimeDelta frame_duration
=
71 audio_timestamp_helper_
->GetFrameDuration(
72 mpeg1audio_frame
.sample_count
);
74 // Emit an audio frame.
75 bool is_key_frame
= true;
77 // TODO(wolenetz/acolwell): Validate and use a common cross-parser TrackId
78 // type and allow multiple audio tracks. See https://crbug.com/341581.
79 scoped_refptr
<StreamParserBuffer
> stream_parser_buffer
=
80 StreamParserBuffer::CopyFrom(
81 mpeg1audio_frame
.data
,
82 mpeg1audio_frame
.size
,
84 DemuxerStream::AUDIO
, 0);
85 stream_parser_buffer
->set_timestamp(current_pts
);
86 stream_parser_buffer
->set_duration(frame_duration
);
87 emit_buffer_cb_
.Run(stream_parser_buffer
);
89 // Update the PTS of the next frame.
90 audio_timestamp_helper_
->AddFrames(mpeg1audio_frame
.sample_count
);
92 // Skip the current frame.
93 SkipMpeg1AudioFrame(mpeg1audio_frame
);
99 void EsParserMpeg1Audio::Flush() {
102 void EsParserMpeg1Audio::ResetInternal() {
103 last_audio_decoder_config_
= AudioDecoderConfig();
106 bool EsParserMpeg1Audio::LookForMpeg1AudioFrame(
107 Mpeg1AudioFrame
* mpeg1audio_frame
) {
110 es_queue_
->Peek(&es
, &es_size
);
112 int max_offset
= es_size
- MPEG1AudioStreamParser::kHeaderSize
;
116 for (int offset
= 0; offset
< max_offset
; offset
++) {
117 const uint8
* cur_buf
= &es
[offset
];
118 if (cur_buf
[0] != 0xff)
121 int remaining_size
= es_size
- offset
;
122 DCHECK_GE(remaining_size
, MPEG1AudioStreamParser::kHeaderSize
);
123 MPEG1AudioStreamParser::Header header
;
124 if (!MPEG1AudioStreamParser::ParseHeader(media_log_
, cur_buf
, &header
))
127 if (remaining_size
< header
.frame_size
) {
128 // Not a full frame: will resume when we have more data.
129 // Remove all the bytes located before the frame header,
130 // these bytes will not be used anymore.
131 es_queue_
->Pop(offset
);
135 // Check whether there is another frame
136 // |frame_size| apart from the current one.
137 if (remaining_size
>= header
.frame_size
+ 1 &&
138 cur_buf
[header
.frame_size
] != 0xff) {
142 es_queue_
->Pop(offset
);
143 es_queue_
->Peek(&mpeg1audio_frame
->data
, &es_size
);
144 mpeg1audio_frame
->queue_offset
= es_queue_
->head();
145 mpeg1audio_frame
->size
= header
.frame_size
;
146 mpeg1audio_frame
->sample_count
= header
.sample_count
;
148 << "MPEG1 audio syncword @ pos=" << mpeg1audio_frame
->queue_offset
149 << " frame_size=" << mpeg1audio_frame
->size
;
151 << "MPEG1 audio header: "
152 << base::HexEncode(mpeg1audio_frame
->data
,
153 MPEG1AudioStreamParser::kHeaderSize
);
157 es_queue_
->Pop(max_offset
);
161 bool EsParserMpeg1Audio::UpdateAudioConfiguration(
162 const uint8
* mpeg1audio_header
) {
163 MPEG1AudioStreamParser::Header header
;
164 if (!MPEG1AudioStreamParser::ParseHeader(media_log_
, mpeg1audio_header
,
169 // TODO(damienv): Verify whether Android playback requires the extra data
170 // field for Mpeg1 audio. If yes, we should generate this field.
171 AudioDecoderConfig
audio_decoder_config(
174 header
.channel_layout
,
179 if (!audio_decoder_config
.Matches(last_audio_decoder_config_
)) {
180 DVLOG(1) << "Sampling frequency: " << header
.sample_rate
;
181 DVLOG(1) << "Channel layout: " << header
.channel_layout
;
182 // Reset the timestamp helper to use a new time scale.
183 if (audio_timestamp_helper_
&&
184 audio_timestamp_helper_
->base_timestamp() != kNoTimestamp()) {
185 base::TimeDelta base_timestamp
= audio_timestamp_helper_
->GetTimestamp();
186 audio_timestamp_helper_
.reset(
187 new AudioTimestampHelper(header
.sample_rate
));
188 audio_timestamp_helper_
->SetBaseTimestamp(base_timestamp
);
190 audio_timestamp_helper_
.reset(
191 new AudioTimestampHelper(header
.sample_rate
));
193 // Audio config notification.
194 last_audio_decoder_config_
= audio_decoder_config
;
195 new_audio_config_cb_
.Run(audio_decoder_config
);
201 void EsParserMpeg1Audio::SkipMpeg1AudioFrame(
202 const Mpeg1AudioFrame
& mpeg1audio_frame
) {
203 DCHECK_EQ(mpeg1audio_frame
.queue_offset
, es_queue_
->head());
204 es_queue_
->Pop(mpeg1audio_frame
.size
);