Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(208)

Side by Side Diff: media/mpeg2/es_parser_adts.cc

Issue 23566013: Mpeg2 TS stream parser for media source. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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/mpeg2/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"
13 #include "media/base/bit_reader.h"
14 #include "media/base/buffers.h"
15 #include "media/base/channel_layout.h"
16 #include "media/base/stream_parser_buffer.h"
17 #include "media/mpeg2/mpeg2ts_common.h"
18
19 namespace {
20 // Adts header is at least 7 bytes (can be 9 bytes).
21 const int kAdtsHeaderMinSize = 7;
acolwell GONE FROM CHROMIUM 2013/09/05 18:29:10 nit: Please use static here and below instead of a
damienv1 2013/09/09 23:29:45 Done.
22
23 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 const int kMaxSupportedFrequencyIndex = 12;
42
43 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 const int kNumberSamplesPerAACFrame = 1024;
56 const int kNumberSamplesPerHeAACFrame = 2048;
acolwell GONE FROM CHROMIUM 2013/09/05 18:29:10 nit: This and the one below do not appear to be ge
damienv1 2013/09/09 23:29:45 Done.
57 const int kNumberSamplesPerAACLcFrame = 960;
58
59 int ExtractAdtsFrameSize(const uint8* adts_header) {
60 int frame_size =
acolwell GONE FROM CHROMIUM 2013/09/05 18:29:10 nit: The local vars aren't needed here and below.
damienv1 2013/09/09 23:29:45 Done.
61 (static_cast<int>(adts_header[5]) >> 5) |
62 (static_cast<int>(adts_header[4]) << 3) |
63 ((static_cast<int>(adts_header[3]) & 0x3) << 11);
64 return frame_size;
65 }
66
67 int ExtractAdtsFrequencyIndex(const uint8* adts_header) {
68 int frequency_index =
69 (adts_header[2] >> 2) & 0xf;
70 return frequency_index;
71 }
72
73 int ExtractAdtsChannelConfig(const uint8* adts_header) {
74 int channel_config =
75 ((adts_header[3] >> 6) & 0x3) |
76 ((adts_header[2] & 0x1) << 2);
77 return channel_config;
78 }
79
80 // Look for an ADTS syncword.
81 // |new_pos| returns
82 // - either the byte position of the ADTS frame (if found)
83 // - or the byte position of 1st byte that was not processed (if not found).
84 // |frame_sz| returns the size of the ADTS frame (if found).
85 // Return whether a syncword was found.
86 bool LookForSyncWord(const uint8* raw_es, int raw_es_size,
87 int pos,
88 int* new_pos, int* frame_sz) {
89 int max_offset = raw_es_size - kAdtsHeaderMinSize;
90 if (max_offset < 0)
91 max_offset = 0;
92
93 for (int offset = pos; offset < max_offset; offset++) {
94 const uint8* cur_buf = &raw_es[offset];
95
96 if ((cur_buf[0] != 0xff) || ((cur_buf[1] & 0xf6) != 0xf0))
97 // The first 12 bits must be 1.
98 // The layer field (2 bits) must be set to 0.
99 continue;
100
101 int frequency_index = ExtractAdtsFrequencyIndex(cur_buf);
102 if (frequency_index > kMaxSupportedFrequencyIndex) {
103 // Frequency index 13 & 14 are reserved
104 // while 15 means that the frequency is explicitly written
105 // (not supported).
106 continue;
acolwell GONE FROM CHROMIUM 2013/09/05 18:29:10 If these aren't supported, shouldn't we trigger an
damienv1 2013/09/09 23:29:45 Done.
107 }
108
109 int frame_size = ExtractAdtsFrameSize(cur_buf);
110 if (frame_size < kAdtsHeaderMinSize)
111 // Too short to be an ADTS frame.
112 continue;
113
114 // Check whether there is another frame
115 // |size| apart from the current one.
116 int remaining_size = raw_es_size - offset;
117 if (remaining_size >= frame_size + 2) {
118 if ((cur_buf[frame_size] != 0xff) ||
119 (cur_buf[frame_size + 1] & 0xf6) != 0xf0)
120 continue;
121 }
122
123 *new_pos = offset;
124 *frame_sz = frame_size;
125 return true;
126 }
127
128 *new_pos = max_offset;
129 return false;
130 }
131
132 } // namespace
133
134 namespace media {
135 namespace mpeg2ts {
136
137 EsParserAdts::EsParserAdts(
138 NewAudioConfigCB new_audio_config_cb,
139 EmitBufferCB emit_buffer_cb)
140 : new_audio_config_cb_(new_audio_config_cb),
141 emit_buffer_cb_(emit_buffer_cb),
142 first_frame_(true),
143 is_audio_config_known_(false),
144 sampling_frequency_(0),
145 channel_configuration_(0) {
146 }
147
148 EsParserAdts::~EsParserAdts() {
149 }
150
151 void EsParserAdts::Parse(const uint8* buf, int size,
152 base::TimeDelta pts,
153 base::TimeDelta dts) {
154 int raw_es_size = 0;
155 const uint8* raw_es = NULL;
156
157 // The incoming PTS applies to the access unit that comes just after
158 // the beginning of |buf|.
159 if (pts != kNoTimestamp()) {
160 es_byte_queue_.Peek(&raw_es, &raw_es_size);
161 pts_list_.push_back(EsPts(raw_es_size, pts));
162 }
163
164 // Copy the input data to the ES buffer.
165 es_byte_queue_.Push(buf, size);
166 es_byte_queue_.Peek(&raw_es, &raw_es_size);
167
168 // Look for every ADTS frame in the ES buffer starting at offset = 0
169 int es_position = 0;
170 int frame_size;
171 while (LookForSyncWord(raw_es, raw_es_size, es_position,
172 &es_position, &frame_size)) {
173 DVLOG(LOG_LEVEL_ES) << "ADTS syncword @ pos=" << es_position
174 << " frame_size=" << frame_size;
175 DVLOG(LOG_LEVEL_ES) << "ADTS header: "
176 << base::HexEncode(&raw_es[es_position], 7);
177
178 // Do not process the frame if this one is a partial frame.
179 int remaining_size = raw_es_size - es_position;
180 if (frame_size > remaining_size) {
acolwell GONE FROM CHROMIUM 2013/09/05 18:29:10 nit: remove unnecessary {}
damienv1 2013/09/09 23:29:45 Done.
181 break;
182 }
183
184 // Update the audio configuration if needed.
185 DCHECK_GE(frame_size, kAdtsHeaderMinSize);
186 UpdateAudioConfiguration(&raw_es[es_position]);
187
188 // Get the PTS & the duration of this access unit.
189 base::TimeDelta current_pts = estimated_pts_;
190 while (!pts_list_.empty() &&
191 pts_list_.front().first <= es_position) {
192 current_pts = pts_list_.front().second;
193 pts_list_.pop_front();
194 }
195 DVLOG(LOG_LEVEL_ES)
196 << "Current PTS: " << current_pts.InMilliseconds()
197 << " Estimated PTS: " << estimated_pts_.InMilliseconds();
198
199 base::TimeDelta frame_duration =
acolwell GONE FROM CHROMIUM 2013/09/05 18:29:10 nit: Use AudioTimestampHelper for these computatio
damienv1 2013/09/09 23:29:45 That's correct. However, I am not sure the AudioTi
200 base::TimeDelta::FromMicroseconds(
201 (1000000 * kNumberSamplesPerAACFrame) / sampling_frequency_);
202
203 // Verify that PTS is increasing.
204 if (!first_frame_ && current_pts < last_frame_pts_) {
acolwell GONE FROM CHROMIUM 2013/09/05 18:29:10 nit: remove first_frame_ & last_frame_pts_ if you
damienv1 2013/09/09 23:29:45 Removed (I used it mainly for my own debugging).
205 LOG(WARNING) << "ADTS: pts not monotonic";
206 }
207 first_frame_ = false;
208 last_frame_pts_ = current_pts;
209
210 // Emit an audio frame.
211 bool is_key_frame = true;
212 scoped_refptr<StreamParserBuffer> stream_parser_buffer =
213 StreamParserBuffer::CopyFrom(
214 &raw_es[es_position],
215 frame_size,
216 is_key_frame);
217 stream_parser_buffer->SetDecodeTimestamp(current_pts);
218 stream_parser_buffer->set_timestamp(current_pts);
219 stream_parser_buffer->set_duration(frame_duration);
220 emit_buffer_cb_.Run(stream_parser_buffer);
221
222 // Update the PTS of the next frame.
223 estimated_pts_ = current_pts + frame_duration;
acolwell GONE FROM CHROMIUM 2013/09/05 18:29:10 Replace this with AudioTimestampHelper for more ac
damienv1 2013/09/09 23:29:45 ok.
224
225 // Skip the current frame.
226 es_position += frame_size;
227 }
228
229 // Discard all the bytes that have been processed.
230 DiscardEs(es_position);
231 }
232
233 void EsParserAdts::Flush() {
234 // All the complete frames have been emitted,
235 // so just clear the ES buffer.
236 es_byte_queue_.Reset();
237 pts_list_.clear();
238 }
239
240 void EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) {
241 int frequency_index = ExtractAdtsFrequencyIndex(adts_header);
242 DCHECK_LE(frequency_index, kMaxSupportedFrequencyIndex);
243
244 // TODO(damienv): support HE-AAC frequency doubling (SBR)
245 // based on the incoming ADTS profile.
246 int samples_per_second = adts_frequency_table[frequency_index];
247
248 int channel_configuration = ExtractAdtsChannelConfig(adts_header);
249 int adts_profile = (adts_header[2] >> 6) & 0x3;
250
251 if (!is_audio_config_known_ ||
252 sampling_frequency_ != samples_per_second ||
253 channel_configuration_ != channel_configuration) {
254 is_audio_config_known_ = true;
255 sampling_frequency_ = samples_per_second;
256 channel_configuration_ = channel_configuration;
257
258 LOG(INFO) << "Sampling frequency: " << samples_per_second;
acolwell GONE FROM CHROMIUM 2013/09/05 18:29:10 nit: Use DVLOG
damienv1 2013/09/09 23:29:45 Done.
259 LOG(INFO) << "Channel config: " << channel_configuration;
260 LOG(INFO) << "Adts profile: " << adts_profile;
261 AudioDecoderConfig audio_decoder_config(
262 kCodecAAC,
263 kSampleFormatS16,
264 adts_channel_layout[channel_configuration],
265 samples_per_second,
266 NULL, 0,
267 false);
268 new_audio_config_cb_.Run(audio_decoder_config);
269 }
270 }
271
272 void EsParserAdts::DiscardEs(int nbytes) {
273 if (nbytes <= 0)
acolwell GONE FROM CHROMIUM 2013/09/05 18:29:10 nit: Add DCHECK_GE(nbytes, 0) since a negative val
damienv1 2013/09/09 23:29:45 Done.
274 return;
275
276 // Adjust the ES position of each PTS.
277 for (EsPtsList::iterator it = pts_list_.begin(); it != pts_list_.end(); ++it)
278 it->first -= nbytes;
279
280 // Discard |nbytes| of ES.
281 es_byte_queue_.Pop(nbytes);
282 }
283
284 } // namespace mpeg2ts
285 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698