OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2013 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. | |
4 | |
5 #include "media/mp2t/es_parser_adts.h" | |
6 | |
7 #include <list> | |
8 | |
9 #include "base/basictypes.h" | |
10 #include "base/logging.h" | |
11 #include "base/strings/string_number_conversions.h" | |
12 #include "media/base/audio_decoder_config.h" | |
acolwell GONE FROM CHROMIUM
2013/09/16 06:19:30
nit: Shouldn't need this now since it is in the he
damienv1
2013/09/17 02:58:22
Done.
| |
13 #include "media/base/audio_timestamp_helper.h" | |
14 #include "media/base/bit_reader.h" | |
15 #include "media/base/buffers.h" | |
16 #include "media/base/channel_layout.h" | |
17 #include "media/base/stream_parser_buffer.h" | |
18 #include "media/mp2t/mp2t_common.h" | |
19 | |
20 // Adts header is at least 7 bytes (can be 9 bytes). | |
21 static const int kAdtsHeaderMinSize = 7; | |
22 | |
23 static const int adts_frequency_table[16] = { | |
24 96000, | |
25 88200, | |
26 64000, | |
27 48000, | |
28 44100, | |
29 32000, | |
30 24000, | |
31 22050, | |
32 16000, | |
33 12000, | |
34 11025, | |
35 8000, | |
36 7350, | |
37 0, | |
38 0, | |
39 0, | |
40 }; | |
41 static const int kMaxSupportedFrequencyIndex = 12; | |
42 | |
43 static media::ChannelLayout adts_channel_layout[8] = { | |
44 media::CHANNEL_LAYOUT_NONE, | |
45 media::CHANNEL_LAYOUT_MONO, | |
46 media::CHANNEL_LAYOUT_STEREO, | |
47 media::CHANNEL_LAYOUT_SURROUND, | |
48 media::CHANNEL_LAYOUT_4_0, | |
49 media::CHANNEL_LAYOUT_5_0_BACK, | |
50 media::CHANNEL_LAYOUT_5_1_BACK, | |
51 media::CHANNEL_LAYOUT_7_1, | |
52 }; | |
53 | |
54 // Number of samples per frame. | |
55 static const int kNumberSamplesPerAACFrame = 1024; | |
56 | |
57 static int ExtractAdtsFrameSize(const uint8* adts_header) { | |
58 return ((static_cast<int>(adts_header[5]) >> 5) | | |
59 (static_cast<int>(adts_header[4]) << 3) | | |
60 ((static_cast<int>(adts_header[3]) & 0x3) << 11)); | |
61 } | |
62 | |
63 static int ExtractAdtsFrequencyIndex(const uint8* adts_header) { | |
64 return ((adts_header[2] >> 2) & 0xf); | |
65 } | |
66 | |
67 static int ExtractAdtsChannelConfig(const uint8* adts_header) { | |
68 return (((adts_header[3] >> 6) & 0x3) | | |
69 ((adts_header[2] & 0x1) << 2)); | |
70 } | |
71 | |
72 // Look for an ADTS syncword. | |
73 // |new_pos| returns | |
74 // - either the byte position of the ADTS frame (if found) | |
75 // - or the byte position of 1st byte that was not processed (if not found). | |
76 // |frame_sz| returns the size of the ADTS frame (if found). | |
77 // Return whether a syncword was found. | |
78 static bool LookForSyncWord(const uint8* raw_es, int raw_es_size, | |
79 int pos, | |
80 int* new_pos, int* frame_sz) { | |
81 int max_offset = raw_es_size - kAdtsHeaderMinSize; | |
acolwell GONE FROM CHROMIUM
2013/09/16 06:19:30
nit:
DCHECK_GE(pos, 0);
DCHECK_LT(pos, raw_es_siz
damienv1
2013/09/17 02:58:22
Done.
Except that this should be DCHECK_LE(pos, ra
| |
82 if (max_offset < 0) | |
83 max_offset = 0; | |
acolwell GONE FROM CHROMIUM
2013/09/16 06:19:30
nit: I think it might be clearer if you just set *
damienv1
2013/09/17 02:58:22
Yes, the new position should remain unchanged if m
| |
84 | |
85 for (int offset = pos; offset < max_offset; offset++) { | |
86 const uint8* cur_buf = &raw_es[offset]; | |
87 | |
88 if ((cur_buf[0] != 0xff) || ((cur_buf[1] & 0xf6) != 0xf0)) | |
89 // The first 12 bits must be 1. | |
90 // The layer field (2 bits) must be set to 0. | |
91 continue; | |
92 | |
93 int frame_size = ExtractAdtsFrameSize(cur_buf); | |
94 if (frame_size < kAdtsHeaderMinSize) | |
95 // Too short to be an ADTS frame. | |
96 continue; | |
97 | |
98 // Check whether there is another frame | |
99 // |size| apart from the current one. | |
100 int remaining_size = raw_es_size - offset; | |
101 if (remaining_size >= frame_size + 2) { | |
102 if ((cur_buf[frame_size] != 0xff) || | |
acolwell GONE FROM CHROMIUM
2013/09/16 06:19:30
nit: Replace this and the check above w/ isSyncWor
damienv1
2013/09/17 02:58:22
Done.
| |
103 (cur_buf[frame_size + 1] & 0xf6) != 0xf0) | |
104 continue; | |
105 } | |
106 | |
107 *new_pos = offset; | |
108 *frame_sz = frame_size; | |
109 return true; | |
110 } | |
111 | |
112 *new_pos = max_offset; | |
113 return false; | |
114 } | |
115 | |
116 namespace media { | |
117 namespace mp2t { | |
118 | |
119 EsParserAdts::EsParserAdts( | |
120 NewAudioConfigCB new_audio_config_cb, | |
121 EmitBufferCB emit_buffer_cb) | |
122 : new_audio_config_cb_(new_audio_config_cb), | |
123 emit_buffer_cb_(emit_buffer_cb) { | |
124 } | |
125 | |
126 EsParserAdts::~EsParserAdts() { | |
127 } | |
128 | |
129 bool EsParserAdts::Parse(const uint8* buf, int size, | |
130 base::TimeDelta pts, | |
131 base::TimeDelta dts) { | |
132 int raw_es_size = 0; | |
133 const uint8* raw_es = NULL; | |
acolwell GONE FROM CHROMIUM
2013/09/16 06:19:30
nit: var init isn't needed here.
damienv1
2013/09/17 02:58:22
Done.
| |
134 | |
135 // The incoming PTS applies to the access unit that comes just after | |
136 // the beginning of |buf|. | |
137 if (pts != kNoTimestamp()) { | |
138 es_byte_queue_.Peek(&raw_es, &raw_es_size); | |
139 pts_list_.push_back(EsPts(raw_es_size, pts)); | |
140 } | |
141 | |
142 // Copy the input data to the ES buffer. | |
143 es_byte_queue_.Push(buf, size); | |
144 es_byte_queue_.Peek(&raw_es, &raw_es_size); | |
145 | |
146 // Look for every ADTS frame in the ES buffer starting at offset = 0 | |
147 int es_position = 0; | |
148 int frame_size; | |
149 while (LookForSyncWord(raw_es, raw_es_size, es_position, | |
150 &es_position, &frame_size)) { | |
151 DVLOG(LOG_LEVEL_ES) << "ADTS syncword @ pos=" << es_position | |
152 << " frame_size=" << frame_size; | |
153 DVLOG(LOG_LEVEL_ES) << "ADTS header: " | |
154 << base::HexEncode(&raw_es[es_position], 7); | |
155 | |
156 // Do not process the frame if this one is a partial frame. | |
157 int remaining_size = raw_es_size - es_position; | |
158 if (frame_size > remaining_size) | |
159 break; | |
160 | |
161 // Update the audio configuration if needed. | |
162 DCHECK_GE(frame_size, kAdtsHeaderMinSize); | |
163 if (!UpdateAudioConfiguration(&raw_es[es_position])) | |
164 return false; | |
165 | |
166 // Get the PTS & the duration of this access unit. | |
167 DCHECK(audio_timestamp_helper_); | |
acolwell GONE FROM CHROMIUM
2013/09/16 06:19:30
nit: No need for DCHECK here since the code will c
damienv1
2013/09/17 02:58:22
Done.
| |
168 while (!pts_list_.empty() && | |
169 pts_list_.front().first <= es_position) { | |
170 audio_timestamp_helper_->SetBaseTimestamp(pts_list_.front().second); | |
acolwell GONE FROM CHROMIUM
2013/09/16 06:19:30
Is this here to compensate for gaps in the audio?
damienv1
2013/09/17 02:58:22
1) The timestamp helper has a resolution of 1 samp
| |
171 pts_list_.pop_front(); | |
172 } | |
173 | |
174 base::TimeDelta current_pts = audio_timestamp_helper_->GetTimestamp(); | |
175 base::TimeDelta frame_duration = | |
176 audio_timestamp_helper_->GetFrameDuration(kNumberSamplesPerAACFrame); | |
177 | |
178 // Emit an audio frame. | |
179 bool is_key_frame = true; | |
180 scoped_refptr<StreamParserBuffer> stream_parser_buffer = | |
181 StreamParserBuffer::CopyFrom( | |
182 &raw_es[es_position], | |
183 frame_size, | |
184 is_key_frame); | |
185 stream_parser_buffer->SetDecodeTimestamp(current_pts); | |
186 stream_parser_buffer->set_timestamp(current_pts); | |
187 stream_parser_buffer->set_duration(frame_duration); | |
188 emit_buffer_cb_.Run(stream_parser_buffer); | |
189 | |
190 // Update the PTS of the next frame. | |
191 audio_timestamp_helper_->AddFrames(kNumberSamplesPerAACFrame); | |
192 | |
193 // Skip the current frame. | |
194 es_position += frame_size; | |
195 } | |
196 | |
197 // Discard all the bytes that have been processed. | |
198 DiscardEs(es_position); | |
199 | |
200 return true; | |
201 } | |
202 | |
203 void EsParserAdts::Flush() { | |
204 // All the complete frames have been emitted, | |
205 // so just clear the ES buffer. | |
206 es_byte_queue_.Reset(); | |
207 pts_list_.clear(); | |
acolwell GONE FROM CHROMIUM
2013/09/16 06:19:30
This doesn't seem consistent w/ the description in
damienv1
2013/09/17 02:58:22
I made it a noop to match the description.
It does
| |
208 } | |
209 | |
210 void EsParserAdts::Reset() { | |
211 es_byte_queue_.Reset(); | |
212 pts_list_.clear(); | |
213 last_audio_decoder_config_ = AudioDecoderConfig(); | |
214 } | |
215 | |
216 bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) { | |
217 int frequency_index = ExtractAdtsFrequencyIndex(adts_header); | |
218 if (frequency_index > kMaxSupportedFrequencyIndex) { | |
219 // Frequency index 13 & 14 are reserved | |
220 // while 15 means that the frequency is explicitly written | |
221 // (not supported). | |
222 return false; | |
223 } | |
224 | |
225 // TODO(damienv): support HE-AAC frequency doubling (SBR) | |
226 // based on the incoming ADTS profile. | |
227 int samples_per_second = adts_frequency_table[frequency_index]; | |
228 int channel_configuration = ExtractAdtsChannelConfig(adts_header); | |
229 int adts_profile = (adts_header[2] >> 6) & 0x3; | |
230 | |
acolwell GONE FROM CHROMIUM
2013/09/16 06:19:30
I think you need the following since I don't belie
damienv1
2013/09/17 02:58:22
Done.
| |
231 AudioDecoderConfig audio_decoder_config( | |
232 kCodecAAC, | |
233 kSampleFormatS16, | |
234 adts_channel_layout[channel_configuration], | |
235 samples_per_second, | |
236 NULL, 0, | |
237 false); | |
238 | |
239 if (!audio_decoder_config.Matches(last_audio_decoder_config_)) { | |
240 DVLOG(1) << "Sampling frequency: " << samples_per_second; | |
241 DVLOG(1) << "Channel config: " << channel_configuration; | |
242 DVLOG(1) << "Adts profile: " << adts_profile; | |
243 // Reset the timestamp helper to use a new time scale. | |
244 if (audio_timestamp_helper_) { | |
245 base::TimeDelta base_timestamp = audio_timestamp_helper_->GetTimestamp(); | |
246 audio_timestamp_helper_.reset( | |
247 new AudioTimestampHelper(samples_per_second)); | |
248 audio_timestamp_helper_->SetBaseTimestamp(base_timestamp); | |
249 } else { | |
250 audio_timestamp_helper_.reset( | |
251 new AudioTimestampHelper(samples_per_second)); | |
252 } | |
253 // Audio config notification. | |
254 last_audio_decoder_config_ = audio_decoder_config; | |
255 new_audio_config_cb_.Run(audio_decoder_config); | |
256 } | |
257 | |
258 return true; | |
259 } | |
260 | |
261 void EsParserAdts::DiscardEs(int nbytes) { | |
262 DCHECK_GE(nbytes, 0); | |
263 if (nbytes <= 0) | |
264 return; | |
265 | |
266 // Adjust the ES position of each PTS. | |
267 for (EsPtsList::iterator it = pts_list_.begin(); it != pts_list_.end(); ++it) | |
268 it->first -= nbytes; | |
acolwell GONE FROM CHROMIUM
2013/09/16 06:19:30
How big is this list likely to be? It might be mor
damienv1
2013/09/17 02:58:22
In the general case, the size of the list will be
| |
269 | |
270 // Discard |nbytes| of ES. | |
271 es_byte_queue_.Pop(nbytes); | |
272 } | |
273 | |
274 } // namespace mp2t | |
275 } // namespace media | |
276 | |
OLD | NEW |