/src/vlc/modules/packetizer/mpeg4audio.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * mpeg4audio.c: parse and packetize an MPEG 4 audio stream |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2001, 2002, 2006 VLC authors and VideoLAN |
5 | | * |
6 | | * Authors: Laurent Aimar <fenrir@via.ecp.fr> |
7 | | * Gildas Bazin <gbazin@netcourrier.com> |
8 | | * |
9 | | * This program is free software; you can redistribute it and/or modify it |
10 | | * under the terms of the GNU Lesser General Public License as published by |
11 | | * the Free Software Foundation; either version 2.1 of the License, or |
12 | | * (at your option) any later version. |
13 | | * |
14 | | * This program is distributed in the hope that it will be useful, |
15 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | * GNU Lesser General Public License for more details. |
18 | | * |
19 | | * You should have received a copy of the GNU Lesser General Public License |
20 | | * along with this program; if not, write to the Free Software Foundation, |
21 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
22 | | *****************************************************************************/ |
23 | | |
24 | | /***************************************************************************** |
25 | | * Preamble |
26 | | *****************************************************************************/ |
27 | | |
28 | | #ifdef HAVE_CONFIG_H |
29 | | # include "config.h" |
30 | | #endif |
31 | | |
32 | | #include <vlc_common.h> |
33 | | #include <vlc_plugin.h> |
34 | | #include <vlc_codec.h> |
35 | | #include <vlc_block.h> |
36 | | #include <vlc_bits.h> |
37 | | |
38 | | #include <vlc_block_helper.h> |
39 | | #include "packetizer_helper.h" |
40 | | #include "mpeg4audio.h" |
41 | | |
42 | | #include <assert.h> |
43 | | |
44 | | /* AAC Config in ES: |
45 | | * |
46 | | * AudioObjectType 5 bits |
47 | | * samplingFrequencyIndex 4 bits |
48 | | * if (samplingFrequencyIndex == 0xF) |
49 | | * samplingFrequency 24 bits |
50 | | * channelConfiguration 4 bits |
51 | | * GA_SpecificConfig |
52 | | * FrameLengthFlag 1 bit 1024 or 960 |
53 | | * DependsOnCoreCoder 1 bit (always 0) |
54 | | * ExtensionFlag 1 bit (always 0) |
55 | | */ |
56 | | |
57 | | /***************************************************************************** |
58 | | * decoder_sys_t : decoder descriptor |
59 | | *****************************************************************************/ |
60 | | |
61 | | typedef struct |
62 | | { |
63 | | /* |
64 | | * Input properties |
65 | | */ |
66 | | enum vlc_packetizer_state i_state; |
67 | | int i_type; |
68 | | |
69 | | block_bytestream_t bytestream; |
70 | | |
71 | | /* |
72 | | * Common properties |
73 | | */ |
74 | | date_t end_date; |
75 | | vlc_tick_t i_pts; |
76 | | bool b_discontuinity; |
77 | | |
78 | | int i_frame_size; |
79 | | unsigned int i_channels; |
80 | | unsigned int i_rate, i_frame_length, i_header_size; |
81 | | int i_aac_profile; |
82 | | |
83 | | int i_input_rate; |
84 | | |
85 | | /* LOAS */ |
86 | | bool b_latm_cfg; |
87 | | MPEG4_streammux_config_t latm; |
88 | | |
89 | | int i_warnings; |
90 | | } decoder_sys_t; |
91 | | |
92 | | enum |
93 | | { |
94 | | WARN_CRC_UNSUPPORTED = 1 |
95 | | }; |
96 | | |
97 | 1.51k | #define WARN_ONCE(warn, msg) do{\ |
98 | 1.51k | decoder_sys_t *p_sys = p_dec->p_sys;\ |
99 | 1.51k | if( (p_sys->i_warnings & warn) == 0 )\ |
100 | 1.51k | {\ |
101 | 89 | p_sys->i_warnings |= warn;\ |
102 | 89 | msg_Warn( p_dec, msg );\ |
103 | 89 | }\ |
104 | 1.51k | } while(0) |
105 | | |
106 | | enum { |
107 | | TYPE_UNKNOWN, /* AAC samples with[out] headers */ |
108 | | TYPE_UNKNOWN_NONRAW, /* [un]packetized ADTS or LOAS */ |
109 | | TYPE_RAW, /* RAW AAC frames */ |
110 | | TYPE_ADTS, |
111 | | TYPE_LOAS |
112 | | }; |
113 | | |
114 | | static int ChannelConfigurationToVLC(uint8_t i_channel) |
115 | 8.12k | { |
116 | 8.12k | if (i_channel == 7) |
117 | 143 | return 8; // 7.1 |
118 | 7.98k | if (i_channel >= 8) |
119 | 2.29k | return -1; |
120 | 5.68k | return i_channel; |
121 | 7.98k | } |
122 | | |
123 | | static int AOTtoAACProfile(uint8_t i_object_type) |
124 | 249 | { |
125 | 249 | switch(i_object_type) |
126 | 249 | { |
127 | 1 | case MPEG4_AOT_AAC_MAIN: |
128 | 3 | case MPEG4_AOT_AAC_LC: |
129 | 4 | case MPEG4_AOT_AAC_SSR: |
130 | 6 | case MPEG4_AOT_AAC_LTP: |
131 | 6 | case MPEG4_AOT_AAC_SBR: |
132 | 6 | case MPEG4_AOT_AAC_SC: |
133 | 192 | case MPEG4_AOT_ER_AAC_LD: |
134 | 192 | case MPEG4_AOT_AAC_PS: |
135 | 207 | case MPEG4_AOT_ER_AAC_ELD: |
136 | 207 | { |
137 | 207 | static_assert(MPEG4_AOT_AAC_MAIN == AAC_PROFILE_MAIN + 1, |
138 | 207 | "invalid profile to object mapping"); |
139 | 207 | return i_object_type - 1; |
140 | 192 | } |
141 | 42 | default: |
142 | 42 | return -1; |
143 | 249 | } |
144 | 249 | } |
145 | | |
146 | 9.38k | #define ADTS_HEADER_SIZE 9 |
147 | 6.34k | #define LOAS_HEADER_SIZE 3 |
148 | | |
149 | | /**************************************************************************** |
150 | | * Local prototypes |
151 | | ****************************************************************************/ |
152 | | static int OpenPacketizer(vlc_object_t *); |
153 | | static void ClosePacketizer(vlc_object_t *); |
154 | | |
155 | | static block_t *Packetize (decoder_t *, block_t **); |
156 | | static void Flush( decoder_t * ); |
157 | | |
158 | | /***************************************************************************** |
159 | | * Module descriptor |
160 | | *****************************************************************************/ |
161 | 168 | vlc_module_begin () |
162 | 84 | set_subcategory(SUBCAT_SOUT_PACKETIZER) |
163 | 84 | set_description(N_("MPEG4 audio packetizer")) |
164 | 84 | set_capability("audio packetizer", 50) |
165 | 168 | set_callbacks(OpenPacketizer, ClosePacketizer) |
166 | 84 | vlc_module_end () |
167 | | |
168 | | /***************************************************************************** |
169 | | * OpenPacketizer: probe the packetizer and return score |
170 | | *****************************************************************************/ |
171 | | static int OpenPacketizer(vlc_object_t *p_this) |
172 | 2.28M | { |
173 | 2.28M | decoder_t *p_dec = (decoder_t*)p_this; |
174 | 2.28M | decoder_sys_t *p_sys; |
175 | | |
176 | 2.28M | if (p_dec->fmt_in->i_codec != VLC_CODEC_MP4A) |
177 | 2.20M | return VLC_EGENERIC; |
178 | | |
179 | | /* Allocate the memory needed to store the decoder's structure */ |
180 | 83.7k | if ((p_dec->p_sys = p_sys = (decoder_sys_t *)malloc(sizeof(decoder_sys_t))) == NULL) |
181 | 0 | return VLC_ENOMEM; |
182 | | |
183 | | /* Misc init */ |
184 | 83.7k | p_sys->i_state = STATE_NOSYNC; |
185 | 83.7k | p_sys->b_discontuinity = false; |
186 | 83.7k | block_BytestreamInit(&p_sys->bytestream); |
187 | 83.7k | p_sys->i_aac_profile = -1; |
188 | 83.7k | p_sys->b_latm_cfg = false; |
189 | 83.7k | p_sys->i_warnings = 0; |
190 | 83.7k | p_sys->i_channels = 0; |
191 | 83.7k | p_sys->i_rate = 0; |
192 | 83.7k | p_sys->i_frame_length = 0; |
193 | | |
194 | | /* Set output properties */ |
195 | 83.7k | p_dec->fmt_out.i_codec = VLC_CODEC_MP4A; |
196 | | |
197 | 83.7k | msg_Dbg(p_dec, "running MPEG4 audio packetizer"); |
198 | | |
199 | | /* |
200 | | * We need to handle 3 cases. |
201 | | * Case 1 : RAW AAC samples without sync header |
202 | | * The demuxer shouldn't need packetizer, see next case. |
203 | | * Case 2 : AAC samples with ADTS or LOAS/LATM header |
204 | | * Some mux (avi) can't distinguish the both |
205 | | * cases above, and then forwards to packetizer |
206 | | * which should check for header and rewire to case below |
207 | | * Case 3 : Non packetized ADTS or LOAS/LATM |
208 | | * The demuxer needs to set original_codec for hardwiring |
209 | | */ |
210 | | |
211 | 83.7k | switch (p_dec->fmt_in->i_original_fourcc) |
212 | 83.7k | { |
213 | 1.12k | case VLC_FOURCC('L','A','T','M'): |
214 | 1.12k | p_sys->i_type = TYPE_LOAS; |
215 | 1.12k | msg_Dbg(p_dec, "LOAS/LATM Mode"); |
216 | 1.12k | break; |
217 | | |
218 | 252 | case VLC_FOURCC('A','D','T','S'): |
219 | 252 | p_sys->i_type = TYPE_ADTS; |
220 | 252 | msg_Dbg(p_dec, "ADTS Mode"); |
221 | 252 | break; |
222 | | |
223 | 0 | case VLC_FOURCC('H','E','A','D'): |
224 | 0 | p_sys->i_type = TYPE_UNKNOWN_NONRAW; |
225 | 0 | break; |
226 | | |
227 | 82.3k | default: |
228 | 82.3k | p_sys->i_type = TYPE_UNKNOWN; |
229 | 82.3k | break; |
230 | 83.7k | } |
231 | | |
232 | | /* Some mux (avi) do send RAW AAC without extradata, |
233 | | and LATM can be sent with out-of-band audioconfig, |
234 | | (avformat sets m4a extradata in both cases) |
235 | | so we can't rely on extradata to guess multiplexing */ |
236 | 83.7k | p_dec->fmt_out.audio.i_rate = p_dec->fmt_in->audio.i_rate; |
237 | | |
238 | 83.7k | if(p_dec->fmt_in->i_extra) |
239 | 78.8k | { |
240 | 78.8k | MPEG4_asc_t asc; |
241 | 78.8k | bs_t s; |
242 | 78.8k | bs_init(&s, p_dec->fmt_in->p_extra, p_dec->fmt_in->i_extra); |
243 | 78.8k | if(MPEG4_read_AudioSpecificConfig(&s, &asc, true) == VLC_SUCCESS) |
244 | 7.62k | { |
245 | 7.62k | p_dec->fmt_out.audio.i_rate = asc.i_samplerate; |
246 | 7.62k | p_dec->fmt_out.audio.i_frame_length = asc.i_frame_length; |
247 | 7.62k | p_dec->fmt_out.audio.i_channels = |
248 | 7.62k | ChannelConfigurationToVLC(asc.i_channel_configuration); |
249 | 7.62k | if(p_dec->fmt_out.i_profile != -1) |
250 | 0 | p_dec->fmt_out.i_profile = AOTtoAACProfile(asc.i_object_type); |
251 | | |
252 | 7.62k | msg_Dbg(p_dec, "%sAAC%s %dHz %d samples/frame", |
253 | 7.62k | (asc.i_sbr) ? "HE-" : "", |
254 | 7.62k | (asc.i_ps) ? "v2" : "", |
255 | 7.62k | (asc.i_sbr) ? p_dec->fmt_out.audio.i_rate << 1 |
256 | 7.62k | : p_dec->fmt_out.audio.i_rate, |
257 | 7.62k | p_dec->fmt_out.audio.i_frame_length); |
258 | 7.62k | } |
259 | | |
260 | 78.8k | p_dec->fmt_out.p_extra = malloc(p_dec->fmt_in->i_extra); |
261 | 78.8k | if (!p_dec->fmt_out.p_extra) |
262 | 0 | return VLC_ENOMEM; |
263 | 78.8k | p_dec->fmt_out.i_extra = p_dec->fmt_in->i_extra; |
264 | 78.8k | memcpy(p_dec->fmt_out.p_extra, p_dec->fmt_in->p_extra, |
265 | 78.8k | p_dec->fmt_in->i_extra); |
266 | 78.8k | } |
267 | | /* else() We will try to create a AAC Config from adts/loas */ |
268 | | |
269 | 83.7k | date_Init(&p_sys->end_date, p_dec->fmt_out.audio.i_rate ? |
270 | 76.7k | p_dec->fmt_out.audio.i_rate : 48000, 1); |
271 | | |
272 | | /* Set callbacks */ |
273 | 83.7k | p_dec->pf_packetize = Packetize; |
274 | 83.7k | p_dec->pf_flush = Flush; |
275 | 83.7k | p_dec->pf_get_cc = NULL; |
276 | | |
277 | 83.7k | return VLC_SUCCESS; |
278 | 83.7k | } |
279 | | |
280 | | /***************************************************************************** |
281 | | * ClosePacketizer: clean up the packetizer |
282 | | *****************************************************************************/ |
283 | | static void ClosePacketizer(vlc_object_t *p_this) |
284 | 83.7k | { |
285 | 83.7k | decoder_t *p_dec = (decoder_t *)p_this; |
286 | 83.7k | decoder_sys_t *p_sys = p_dec->p_sys; |
287 | | |
288 | 83.7k | block_BytestreamRelease(&p_sys->bytestream); |
289 | 83.7k | free(p_sys); |
290 | 83.7k | } |
291 | | |
292 | | /**************************************************************************** |
293 | | * ForwardRawBlock: |
294 | | **************************************************************************** |
295 | | * This function must be fed with complete frames. |
296 | | ****************************************************************************/ |
297 | | static block_t *ForwardRawBlock(decoder_t *p_dec, block_t **pp_block) |
298 | 126k | { |
299 | 126k | decoder_sys_t *p_sys = p_dec->p_sys; |
300 | 126k | block_t *p_block; |
301 | | |
302 | 126k | if (!pp_block || !*pp_block) |
303 | 64.6k | return NULL; |
304 | | |
305 | 61.9k | p_block = *pp_block; |
306 | 61.9k | *pp_block = NULL; /* Don't reuse this block */ |
307 | | |
308 | 61.9k | vlc_tick_t i_diff = 0; |
309 | 61.9k | if (p_block->i_pts != VLC_TICK_INVALID && |
310 | 53.9k | p_block->i_pts != date_Get(&p_sys->end_date)) |
311 | 31.2k | { |
312 | 31.2k | if(date_Get(&p_sys->end_date) != VLC_TICK_INVALID) |
313 | 27.0k | i_diff = llabs( date_Get(&p_sys->end_date) - p_block->i_pts ); |
314 | 31.2k | date_Set(&p_sys->end_date, p_block->i_pts); |
315 | 31.2k | } |
316 | | |
317 | 61.9k | p_block->i_pts = p_block->i_dts = date_Get(&p_sys->end_date); |
318 | | |
319 | | /* Might not be known due to missing extradata, |
320 | | will be set to block pts above */ |
321 | 61.9k | if(p_dec->fmt_out.audio.i_frame_length && p_block->i_pts != VLC_TICK_INVALID) |
322 | 44.5k | { |
323 | 44.5k | p_block->i_length = date_Increment(&p_sys->end_date, |
324 | 44.5k | p_dec->fmt_out.audio.i_frame_length) - p_block->i_pts; |
325 | | |
326 | 44.5k | if( i_diff > p_block->i_length ) |
327 | 5.49k | p_sys->b_discontuinity = true; |
328 | 44.5k | } |
329 | | |
330 | 61.9k | return p_block; |
331 | 126k | } |
332 | | |
333 | | /**************************************************************************** |
334 | | * ADTS helpers |
335 | | ****************************************************************************/ |
336 | | static int ADTSSyncInfo(decoder_t * p_dec, const uint8_t * p_buf, |
337 | | unsigned int * pi_channels, |
338 | | unsigned int * pi_sample_rate, |
339 | | unsigned int * pi_frame_length, |
340 | | unsigned int * pi_header_size) |
341 | 9.27k | { |
342 | 9.27k | int i_profile, i_sample_rate_idx, i_frame_size; |
343 | 9.27k | bool b_crc; |
344 | | |
345 | | /* Fixed header between frames */ |
346 | | //int i_id = ((p_buf[1] >> 3) & 0x01) ? 2 : 4; /* MPEG-2 or 4 */ |
347 | 9.27k | b_crc = !(p_buf[1] & 0x01); |
348 | 9.27k | i_profile = p_buf[2] >> 6; |
349 | 9.27k | i_sample_rate_idx = (p_buf[2] >> 2) & 0x0f; |
350 | 9.27k | *pi_sample_rate = pi_sample_rates[i_sample_rate_idx]; |
351 | | //private_bit = (p_buf[2] >> 1) & 0x01; |
352 | 9.27k | *pi_channels = ((p_buf[2] & 0x01) << 2) | ((p_buf[3] >> 6) & 0x03); |
353 | 9.27k | if (*pi_channels == 0) /* workaround broken streams */ |
354 | 776 | *pi_channels = 2; |
355 | | //original_copy = (p_buf[3] >> 5) & 0x01; |
356 | | //home = (p_buf[3] >> 4) & 0x01; |
357 | | |
358 | | /* Variable header */ |
359 | | //copyright_id_bit = (p_buf[3] >> 3) & 0x01; |
360 | | //copyright_id_start = (p_buf[3] >> 2) & 0x01; |
361 | 9.27k | i_frame_size = ((p_buf[3] & 0x03) << 11) | (p_buf[4] << 3) | |
362 | 9.27k | ((p_buf[5] >> 5) /*& 0x7*/); |
363 | | //uint16_t buffer_fullness = ((p_buf[5] & 0x1f) << 6) | (p_buf[6] >> 2); |
364 | 9.27k | unsigned short i_raw_blocks_in_frame = p_buf[6] & 0x03; |
365 | | |
366 | 9.27k | if (!*pi_sample_rate || !i_frame_size) { |
367 | 655 | msg_Warn(p_dec, "Invalid ADTS header"); |
368 | 655 | return 0; |
369 | 655 | } |
370 | | |
371 | 8.61k | *pi_frame_length = 1024; |
372 | | |
373 | 8.61k | if (i_raw_blocks_in_frame == 0) { |
374 | 5.35k | if (b_crc) { |
375 | 1.51k | WARN_ONCE(WARN_CRC_UNSUPPORTED, "ADTS CRC not supported"); |
376 | | //uint16_t crc = (p_buf[7] << 8) | p_buf[8]; |
377 | 1.51k | } |
378 | 5.35k | } else { |
379 | 3.25k | msg_Err(p_dec, "Multiple blocks per frame in ADTS not supported"); |
380 | 3.25k | return 0; |
381 | | #if 0 |
382 | | int i; |
383 | | const uint8_t *p_pos = p_buf + 7; |
384 | | uint16_t crc_block; |
385 | | uint16_t i_block_pos[3]; |
386 | | if (b_crc) { |
387 | | for (i = 0 ; i < i_raw_blocks_in_frame ; i++) { |
388 | | /* the 1st block's position is known ... */ |
389 | | i_block_pos[i] = (*p_pos << 8) | *(p_pos+1); |
390 | | p_pos += 2; |
391 | | } |
392 | | crc_block = (*p_pos << 8) | *(p_pos+1); |
393 | | p_pos += 2; |
394 | | } |
395 | | for (i = 0 ; i <= i_raw_blocks_in_frame ; i++) { |
396 | | //read 1 block |
397 | | if (b_crc) { |
398 | | WARN_ONCE(WARN_CRC_UNSUPPORTED, "ADTS CRC not supported"); |
399 | | //uint16_t crc = (*p_pos << 8) | *(p_pos+1); |
400 | | //p_pos += 2; |
401 | | } |
402 | | } |
403 | | #endif |
404 | 3.25k | } |
405 | | |
406 | | |
407 | | /* Build the decoder specific info header */ |
408 | 5.35k | if (!p_dec->fmt_out.i_extra) { |
409 | 283 | p_dec->fmt_out.p_extra = malloc(2); |
410 | 283 | if (!p_dec->fmt_out.p_extra) |
411 | 0 | return 0; |
412 | 283 | p_dec->fmt_out.i_extra = 2; |
413 | 283 | ((uint8_t *)p_dec->fmt_out.p_extra)[0] = |
414 | 283 | (i_profile + 1) << 3 | (i_sample_rate_idx >> 1); |
415 | 283 | ((uint8_t *)p_dec->fmt_out.p_extra)[1] = |
416 | 283 | ((i_sample_rate_idx & 0x01) << 7) | (*pi_channels <<3); |
417 | 283 | } |
418 | | |
419 | | /* ADTS header length */ |
420 | 5.35k | *pi_header_size = b_crc ? 9 : 7; |
421 | | |
422 | 5.35k | return i_frame_size - *pi_header_size; |
423 | 5.35k | } |
424 | | |
425 | | /**************************************************************************** |
426 | | * LOAS helpers |
427 | | ****************************************************************************/ |
428 | | static int LOASSyncInfo(uint8_t p_header[LOAS_HEADER_SIZE], unsigned int *pi_header_size) |
429 | 6.34k | { |
430 | 6.34k | *pi_header_size = 3; |
431 | 6.34k | return ((p_header[1] & 0x1f) << 8) + p_header[2]; |
432 | 6.34k | } |
433 | | |
434 | | static int LOASParse(decoder_t *p_dec, uint8_t *p_buffer, int i_buffer) |
435 | 484 | { |
436 | 484 | decoder_sys_t *p_sys = p_dec->p_sys; |
437 | 484 | bs_t s; |
438 | 484 | int i_accumulated = 0; |
439 | | |
440 | 484 | bs_init(&s, p_buffer, i_buffer); |
441 | | |
442 | | /* Read the stream mux configuration if present */ |
443 | 484 | if (!bs_read1(&s) && !MPEG4_parse_StreamMuxConfig(&s, &p_sys->latm) && |
444 | 254 | p_sys->latm.i_streams > 0) { |
445 | 254 | const MPEG4_audio_stream_t *st = &p_sys->latm.stream[0]; |
446 | | |
447 | 254 | if(st->cfg.i_samplerate == 0 || st->cfg.i_frame_length == 0 || |
448 | 251 | ChannelConfigurationToVLC(st->cfg.i_channel_configuration) == 0) |
449 | 5 | return 0; |
450 | | |
451 | 249 | p_sys->i_channels = ChannelConfigurationToVLC(st->cfg.i_channel_configuration); |
452 | 249 | p_sys->i_rate = st->cfg.i_samplerate; |
453 | 249 | p_sys->i_frame_length = st->cfg.i_frame_length; |
454 | 249 | p_sys->i_aac_profile = AOTtoAACProfile(st->cfg.i_object_type); |
455 | | |
456 | 249 | if (p_sys->i_channels && p_sys->i_rate && p_sys->i_frame_length > 0) |
457 | 249 | { |
458 | 249 | if(p_dec->fmt_out.i_extra != st->i_extra || |
459 | 54 | (p_dec->fmt_out.i_extra > 0 && |
460 | 54 | memcmp(p_dec->fmt_out.p_extra, st->extra, st->i_extra)) ) |
461 | 196 | { |
462 | 196 | if(p_dec->fmt_out.i_extra) |
463 | 6 | free(p_dec->fmt_out.p_extra); |
464 | 196 | p_dec->fmt_out.p_extra = st->i_extra ? malloc(st->i_extra) : NULL; |
465 | 196 | if(p_dec->fmt_out.p_extra) |
466 | 196 | { |
467 | 196 | p_dec->fmt_out.i_extra = st->i_extra; |
468 | 196 | memcpy(p_dec->fmt_out.p_extra, st->extra, st->i_extra); |
469 | 196 | p_sys->b_latm_cfg = true; |
470 | 196 | } |
471 | 0 | else |
472 | 0 | { |
473 | 0 | p_dec->fmt_out.i_extra = 0; |
474 | 0 | p_sys->b_latm_cfg = false; |
475 | 0 | } |
476 | 196 | } |
477 | 249 | } |
478 | 249 | } |
479 | | |
480 | | /* Wait for the configuration */ |
481 | 479 | if (!p_sys->b_latm_cfg) |
482 | 137 | { |
483 | | /* WAVE_FORMAT_MPEG_LOAS, configuration provided as AAC header :/ */ |
484 | 137 | if( p_dec->fmt_in->i_extra > 0 && |
485 | 1 | p_sys->i_channels && p_sys->i_rate && p_sys->i_frame_length ) |
486 | 0 | { |
487 | 0 | p_sys->b_latm_cfg = true; |
488 | 0 | } |
489 | 137 | else return 0; |
490 | 137 | } |
491 | | |
492 | 342 | if(bs_eof(&s) && i_buffer) |
493 | 45 | goto truncated; |
494 | | |
495 | | /* FIXME do we need to split the subframe into independent packet ? */ |
496 | 297 | if (p_sys->latm.numSubFrames != 0) |
497 | 297 | msg_Err(p_dec, "latm sub frames not yet supported, please send a sample"); |
498 | | |
499 | 7.40k | for (uint8_t i_sub = 0; i_sub <= p_sys->latm.numSubFrames; i_sub++) { |
500 | 7.13k | unsigned pi_payload[MPEG4_STREAMMUX_MAX_PROGRAM][MPEG4_STREAMMUX_MAX_LAYER]; |
501 | 7.13k | if (p_sys->latm.allStreamsSameTimeFraming) { |
502 | | /* Payload length */ |
503 | 43.3k | for (uint8_t i_program = 0; i_program <= p_sys->latm.numProgram; i_program++) { |
504 | 217k | for (uint8_t i_layer = 0; i_layer <= p_sys->latm.p_numLayer[i_program]; i_layer++) { |
505 | 178k | const MPEG4_audio_stream_t *st = &p_sys->latm.stream[p_sys->latm.pi_stream[i_program][i_layer]]; |
506 | 178k | if (st->i_frame_length_type == 0) { |
507 | 28.0k | unsigned i_payload = 0; |
508 | 29.4k | for (;;) { |
509 | 29.4k | uint8_t i_tmp = bs_read(&s, 8); |
510 | 29.4k | i_payload += i_tmp; |
511 | 29.4k | if (i_tmp != 255) |
512 | 28.0k | break; |
513 | 29.4k | } |
514 | 28.0k | pi_payload[i_program][i_layer] = i_payload; |
515 | 150k | } else if (st->i_frame_length_type == 1) { |
516 | 22.1k | pi_payload[i_program][i_layer] = st->i_frame_length / 8; /* XXX not correct */ |
517 | 128k | } else if ((st->i_frame_length_type == 3) || |
518 | 110k | (st->i_frame_length_type == 5) || |
519 | 100k | (st->i_frame_length_type == 7)) { |
520 | 79.0k | bs_skip(&s, 2); // muxSlotLengthCoded |
521 | 79.0k | pi_payload[i_program][i_layer] = 0; /* TODO */ |
522 | 79.0k | } else { |
523 | 49.3k | pi_payload[i_program][i_layer] = 0; /* TODO */ |
524 | 49.3k | } |
525 | 178k | } |
526 | 39.0k | } |
527 | | |
528 | | /* Payload Data */ |
529 | 43.0k | for (uint8_t i_program = 0; i_program <= p_sys->latm.numProgram; i_program++) { |
530 | 43.0k | for (uint8_t i_layer = 0; i_layer <= p_sys->latm.p_numLayer[i_program]; i_layer++) { |
531 | | /* XXX we only extract 1 stream */ |
532 | 38.9k | if (i_program != 0 || i_layer != 0) |
533 | 34.6k | break; |
534 | | |
535 | 4.28k | if (pi_payload[i_program][i_layer] <= 0) |
536 | 3.20k | continue; |
537 | | |
538 | | /* FIXME that's slow (and a bit ugly to write in place) */ |
539 | 99.4k | for (unsigned i = 0; i < pi_payload[i_program][i_layer]; i++) { |
540 | 98.4k | if (i_accumulated >= i_buffer) |
541 | 0 | return 0; |
542 | 98.4k | p_buffer[i_accumulated++] = bs_read(&s, 8); |
543 | 98.4k | if(bs_error(&s)) |
544 | 35 | goto truncated; |
545 | 98.4k | } |
546 | 1.08k | } |
547 | 38.8k | } |
548 | 4.28k | } else { |
549 | 2.85k | const int i_chunks = bs_read(&s, 4); |
550 | | #if 0 |
551 | | int pi_program[16]; |
552 | | int pi_layer[16]; |
553 | | #endif |
554 | | |
555 | 2.85k | msg_Err(p_dec, "latm without same time frameing not yet supported, please send a sample"); |
556 | | |
557 | 19.8k | for (int i_chunk = 0; i_chunk < i_chunks; i_chunk++) { |
558 | 17.0k | const int streamIndex = bs_read(&s, 4); |
559 | 17.0k | const MPEG4_audio_stream_t *st = &p_sys->latm.stream[streamIndex]; |
560 | 17.0k | const int i_program = st->i_program; |
561 | 17.0k | const int i_layer = st->i_layer; |
562 | | |
563 | | #if 0 |
564 | | pi_program[i_chunk] = i_program; |
565 | | pi_layer[i_chunk] = i_layer; |
566 | | #endif |
567 | | |
568 | 17.0k | if (st->i_frame_length_type == 0) { |
569 | 8.44k | int i_payload = 0; |
570 | 9.73k | for (;;) { |
571 | 9.73k | int i_tmp = bs_read(&s, 8); |
572 | 9.73k | i_payload += i_tmp; |
573 | 9.73k | if (i_tmp != 255) |
574 | 8.44k | break; |
575 | 9.73k | } |
576 | 8.44k | pi_payload[i_program][i_layer] = i_payload; |
577 | 8.44k | bs_skip(&s, 1); // auEndFlag |
578 | 8.59k | } else if (st->i_frame_length_type == 1) { |
579 | 407 | pi_payload[i_program][i_layer] = st->i_frame_length / 8; /* XXX not correct */ |
580 | 8.18k | } else if ((st->i_frame_length_type == 3) || |
581 | 5.81k | (st->i_frame_length_type == 5) || |
582 | 5.48k | (st->i_frame_length_type == 7)) { |
583 | 4.16k | bs_read(&s, 2); // muxSlotLengthCoded |
584 | 4.16k | } |
585 | 17.0k | } |
586 | | #if 0 |
587 | | for (int i_chunk = 0; i_chunk < i_chunks; i_chunk++) { |
588 | | //const int i_program = pi_program[i_chunk]; |
589 | | //const int i_layer = pi_layer[i_chunk]; |
590 | | |
591 | | /* TODO ? Payload */ |
592 | | } |
593 | | #endif |
594 | 2.85k | } |
595 | 7.13k | } |
596 | | |
597 | | |
598 | 262 | bs_skip(&s, p_sys->latm.otherDataLenBits); |
599 | 262 | bs_align(&s); |
600 | | |
601 | 262 | return i_accumulated; |
602 | | |
603 | 80 | truncated: |
604 | 80 | msg_Warn(p_dec,"Truncated LOAS packet. Wrong format ?"); |
605 | 80 | return 0; |
606 | 297 | } |
607 | | |
608 | | /***************************************************************************** |
609 | | * |
610 | | *****************************************************************************/ |
611 | | static void SetupOutput(decoder_t *p_dec, block_t *p_block) |
612 | 186 | { |
613 | 186 | decoder_sys_t *p_sys = p_dec->p_sys; |
614 | | |
615 | 186 | if (p_dec->fmt_out.audio.i_rate != p_sys->i_rate && p_sys->i_rate > 0) |
616 | 61 | { |
617 | 61 | msg_Info(p_dec, "AAC channels: %d samplerate: %d", |
618 | 61 | p_sys->i_channels, p_sys->i_rate); |
619 | 61 | date_Change(&p_sys->end_date, p_sys->i_rate, 1); |
620 | 61 | } |
621 | | |
622 | 186 | p_dec->fmt_out.audio.i_rate = p_sys->i_rate; |
623 | 186 | p_dec->fmt_out.audio.i_channels = p_sys->i_channels; |
624 | 186 | p_dec->fmt_out.audio.i_bytes_per_frame = p_sys->i_frame_size; |
625 | 186 | p_dec->fmt_out.audio.i_frame_length = p_sys->i_frame_length; |
626 | | /* Will reload extradata on change */ |
627 | 186 | p_dec->fmt_out.i_profile = p_sys->i_aac_profile; |
628 | | |
629 | | #if 0 |
630 | | p_dec->fmt_out.audio.i_physical_channels = p_sys->i_channels_conf; |
631 | | #endif |
632 | | |
633 | 186 | p_block->i_pts = p_block->i_dts = date_Get(&p_sys->end_date); |
634 | | |
635 | 186 | p_block->i_length = |
636 | 186 | date_Increment(&p_sys->end_date, p_sys->i_frame_length) - p_block->i_pts; |
637 | 186 | } |
638 | | |
639 | | /***************************************************************************** |
640 | | * FlushStreamBlock: |
641 | | *****************************************************************************/ |
642 | | static void Flush(decoder_t *p_dec) |
643 | 4.15k | { |
644 | 4.15k | decoder_sys_t *p_sys = p_dec->p_sys; |
645 | | |
646 | 4.15k | p_sys->i_state = STATE_NOSYNC; |
647 | 4.15k | block_BytestreamEmpty(&p_sys->bytestream); |
648 | 4.15k | date_Set(&p_sys->end_date, VLC_TICK_INVALID); |
649 | 4.15k | p_sys->b_discontuinity = true; |
650 | 4.15k | } |
651 | | |
652 | | static inline bool HasADTSHeader( const uint8_t *p_header ) |
653 | 3.42M | { |
654 | 3.42M | return p_header[0] == 0xff && (p_header[1] & 0xf6) == 0xf0; |
655 | 3.42M | } |
656 | | |
657 | | static inline bool HasLoasHeader( const uint8_t *p_header ) |
658 | 1.31M | { |
659 | 1.31M | return p_header[0] == 0x56 && (p_header[1] & 0xe0) == 0xe0; |
660 | 1.31M | } |
661 | | |
662 | | /**************************************************************************** |
663 | | * PacketizeStreamBlock: ADTS/LOAS packetizer |
664 | | ****************************************************************************/ |
665 | | static block_t *PacketizeStreamBlock(decoder_t *p_dec, block_t **pp_block) |
666 | 71.0k | { |
667 | 71.0k | decoder_sys_t *p_sys = p_dec->p_sys; |
668 | 71.0k | uint8_t p_header[ADTS_HEADER_SIZE + LOAS_HEADER_SIZE]; |
669 | 71.0k | block_t *p_out_buffer; |
670 | 71.0k | uint8_t *p_buf; |
671 | | |
672 | 71.0k | block_t *p_block = pp_block ? *pp_block : NULL; |
673 | | |
674 | 71.0k | if(p_block) |
675 | 63.4k | { |
676 | 63.4k | block_BytestreamPush(&p_sys->bytestream, p_block); |
677 | 63.4k | *pp_block = NULL; |
678 | 63.4k | } |
679 | | |
680 | 102k | for (;;) switch(p_sys->i_state) { |
681 | 36.0k | case STATE_NOSYNC: |
682 | 4.74M | while (block_PeekBytes(&p_sys->bytestream, p_header, 2) == VLC_SUCCESS) { |
683 | | /* Look for sync word - should be 0xfff(adts) or 0x2b7(loas) */ |
684 | 4.72M | if ((p_sys->i_type == TYPE_ADTS || p_sys->i_type == TYPE_UNKNOWN_NONRAW) && |
685 | 3.42M | HasADTSHeader( p_header ) ) |
686 | 9.27k | { |
687 | 9.27k | if (p_sys->i_type != TYPE_ADTS) |
688 | 9.27k | msg_Dbg(p_dec, "detected ADTS format"); |
689 | | |
690 | 9.27k | p_sys->i_state = STATE_SYNC; |
691 | 9.27k | p_sys->i_type = TYPE_ADTS; |
692 | 9.27k | break; |
693 | 9.27k | } |
694 | 4.71M | else if ((p_sys->i_type == TYPE_LOAS || p_sys->i_type == TYPE_UNKNOWN_NONRAW) && |
695 | 1.30M | HasLoasHeader( p_header ) ) |
696 | 6.34k | { |
697 | 6.34k | if (p_sys->i_type != TYPE_LOAS) |
698 | 6.34k | msg_Dbg(p_dec, "detected LOAS format"); |
699 | | |
700 | 6.34k | p_sys->i_state = STATE_SYNC; |
701 | 6.34k | p_sys->i_type = TYPE_LOAS; |
702 | 6.34k | break; |
703 | 6.34k | } |
704 | 4.71M | (void) block_SkipByte(&p_sys->bytestream); |
705 | 4.71M | } |
706 | 36.0k | if (p_sys->i_state != STATE_SYNC) { |
707 | 20.4k | block_BytestreamFlush(&p_sys->bytestream); |
708 | | |
709 | | /* Need more data */ |
710 | 20.4k | return NULL; |
711 | 20.4k | } |
712 | | /* fallthrough */ |
713 | | |
714 | 15.6k | case STATE_SYNC: |
715 | | /* New frame, set the Presentation Time Stamp */ |
716 | 15.6k | p_sys->i_pts = p_sys->bytestream.p_block->i_pts; |
717 | 15.6k | if (p_sys->i_pts != VLC_TICK_INVALID && |
718 | 12.5k | p_sys->i_pts != date_Get(&p_sys->end_date)) |
719 | 3.45k | date_Set(&p_sys->end_date, p_sys->i_pts); |
720 | 15.6k | p_sys->i_state = STATE_HEADER; |
721 | 15.6k | break; |
722 | | |
723 | 15.7k | case STATE_HEADER: |
724 | 15.7k | if (p_sys->i_type == TYPE_ADTS) { |
725 | | /* Get ADTS frame header (ADTS_HEADER_SIZE bytes) */ |
726 | 9.38k | if (block_PeekBytes(&p_sys->bytestream, p_header, |
727 | 9.38k | ADTS_HEADER_SIZE) != VLC_SUCCESS) |
728 | 116 | return NULL; /* Need more data */ |
729 | | |
730 | | /* Check if frame is valid and get frame info */ |
731 | 9.27k | p_sys->i_frame_size = ADTSSyncInfo(p_dec, p_header, |
732 | 9.27k | &p_sys->i_channels, |
733 | 9.27k | &p_sys->i_rate, |
734 | 9.27k | &p_sys->i_frame_length, |
735 | 9.27k | &p_sys->i_header_size); |
736 | 9.27k | } else { |
737 | 6.34k | assert(p_sys->i_type == TYPE_LOAS); |
738 | | /* Get LOAS frame header (LOAS_HEADER_SIZE bytes) */ |
739 | 6.34k | if (block_PeekBytes(&p_sys->bytestream, p_header, |
740 | 6.34k | LOAS_HEADER_SIZE) != VLC_SUCCESS) |
741 | 5 | return NULL; /* Need more data */ |
742 | | |
743 | | /* Check if frame is valid and get frame info */ |
744 | 6.34k | p_sys->i_frame_size = LOASSyncInfo(p_header, &p_sys->i_header_size); |
745 | 6.34k | } |
746 | | |
747 | 15.6k | if (p_sys->i_frame_size <= 0) { |
748 | 3.92k | msg_Dbg(p_dec, "emulated sync word"); |
749 | 3.92k | block_SkipByte(&p_sys->bytestream); |
750 | 3.92k | p_sys->i_state = STATE_NOSYNC; |
751 | 3.92k | break; |
752 | 3.92k | } |
753 | | |
754 | 11.6k | p_sys->i_state = STATE_NEXT_SYNC; |
755 | | /* fallthrough */ |
756 | | |
757 | 60.7k | case STATE_NEXT_SYNC: |
758 | 60.7k | if (p_sys->bytestream.p_block == NULL) { |
759 | 0 | p_sys->i_state = STATE_NOSYNC; |
760 | 0 | block_BytestreamFlush(&p_sys->bytestream); |
761 | 0 | return NULL; |
762 | 0 | } |
763 | | |
764 | | /* Check if next expected frame contains the sync word */ |
765 | 60.7k | if (block_PeekOffsetBytes(&p_sys->bytestream, p_sys->i_frame_size |
766 | 60.7k | + p_sys->i_header_size, p_header, 2) != VLC_SUCCESS) |
767 | 50.3k | { |
768 | 50.3k | if(p_block == NULL) /* drain */ |
769 | 1.23k | { |
770 | 1.23k | p_sys->i_state = STATE_SEND_DATA; |
771 | 1.23k | break; |
772 | 1.23k | } |
773 | 49.0k | return NULL; /* Need more data */ |
774 | 50.3k | } |
775 | | |
776 | 60.7k | assert((p_sys->i_type == TYPE_ADTS) || (p_sys->i_type == TYPE_LOAS)); |
777 | 10.4k | if ( (p_sys->i_type == TYPE_ADTS && !HasADTSHeader( p_header )) || |
778 | 5.46k | (p_sys->i_type == TYPE_LOAS && !HasLoasHeader( p_header )) ) |
779 | 10.3k | { |
780 | | /* Check spacial padding case. Failing if need more bytes is ok since |
781 | | that should have been sent as a whole block */ |
782 | 10.3k | if( block_PeekOffsetBytes(&p_sys->bytestream, |
783 | 10.3k | p_sys->i_frame_size + p_sys->i_header_size, |
784 | 10.3k | p_header, 3) == VLC_SUCCESS && |
785 | 10.1k | p_header[0] == 0x00 && |
786 | 904 | ((p_sys->i_type == TYPE_ADTS && HasADTSHeader( &p_header[1] )) || |
787 | 803 | (p_sys->i_type == TYPE_LOAS && !HasLoasHeader( &p_header[1] )))) |
788 | 530 | { |
789 | 530 | p_sys->i_state = STATE_SEND_DATA; |
790 | 530 | } |
791 | 9.82k | else |
792 | 9.82k | { |
793 | 9.82k | msg_Dbg(p_dec, "emulated sync word (no sync on following frame)" |
794 | 9.82k | " 0x%"PRIx8" 0x%"PRIx8, p_header[0], p_header[1] ); |
795 | 9.82k | p_sys->i_state = STATE_NOSYNC; |
796 | 9.82k | block_SkipByte(&p_sys->bytestream); |
797 | 9.82k | } |
798 | 10.3k | break; |
799 | 10.3k | } |
800 | | |
801 | 104 | p_sys->i_state = STATE_SEND_DATA; |
802 | 104 | break; |
803 | | |
804 | 0 | case STATE_GET_DATA: |
805 | | /* Make sure we have enough data. |
806 | | * (Not useful if we went through NEXT_SYNC) */ |
807 | 0 | if (block_WaitBytes(&p_sys->bytestream, p_sys->i_frame_size + |
808 | 0 | p_sys->i_header_size) != VLC_SUCCESS) |
809 | 0 | return NULL; /* Need more data */ |
810 | 0 | p_sys->i_state = STATE_SEND_DATA; |
811 | | /* fallthrough */ |
812 | |
|
813 | 1.87k | case STATE_SEND_DATA: |
814 | | /* When we reach this point we already know we have enough |
815 | | * data available. */ |
816 | | |
817 | 1.87k | if (unlikely( block_BytestreamRemaining(&p_sys->bytestream) < |
818 | 1.87k | p_sys->i_header_size + p_sys->i_frame_size )) |
819 | 1.23k | return NULL; // need more data |
820 | | |
821 | 642 | p_out_buffer = block_Alloc(p_sys->i_frame_size); |
822 | 642 | if (!p_out_buffer) { |
823 | 0 | return NULL; |
824 | 0 | } |
825 | 642 | p_buf = p_out_buffer->p_buffer; |
826 | | |
827 | | /* Skip the ADTS/LOAS header */ |
828 | 642 | (void) block_SkipBytes(&p_sys->bytestream, p_sys->i_header_size); |
829 | | |
830 | | /* Copy the whole frame into the buffer */ |
831 | 642 | (void) block_GetBytes(&p_sys->bytestream, p_buf, p_sys->i_frame_size); |
832 | 642 | if (p_sys->i_type != TYPE_ADTS) { /* parse/extract the whole frame */ |
833 | 484 | assert(p_sys->i_type == TYPE_LOAS); |
834 | 484 | p_out_buffer->i_buffer = LOASParse(p_dec, p_buf, p_sys->i_frame_size); |
835 | 484 | if (p_out_buffer->i_buffer <= 0) |
836 | 456 | { |
837 | 456 | if (!p_sys->b_latm_cfg) |
838 | 456 | msg_Warn(p_dec, "waiting for header"); |
839 | | |
840 | 456 | block_Release(p_out_buffer); |
841 | 456 | p_out_buffer = NULL; |
842 | 456 | p_sys->i_state = STATE_NOSYNC; |
843 | 456 | break; |
844 | 456 | } |
845 | 484 | } |
846 | 186 | SetupOutput(p_dec, p_out_buffer); |
847 | | /* Make sure we don't reuse the same pts twice */ |
848 | 186 | if (p_sys->i_pts == p_sys->bytestream.p_block->i_pts) |
849 | 142 | p_sys->i_pts = p_sys->bytestream.p_block->i_pts = VLC_TICK_INVALID; |
850 | | |
851 | | /* So p_block doesn't get re-added several times */ |
852 | 186 | if( pp_block ) |
853 | 178 | *pp_block = block_BytestreamPop(&p_sys->bytestream); |
854 | | |
855 | 186 | p_sys->i_state = STATE_NOSYNC; |
856 | | |
857 | 186 | return p_out_buffer; |
858 | | |
859 | 0 | case STATE_CUSTOM_FIRST: |
860 | 0 | break; // do nothing |
861 | 102k | } |
862 | | |
863 | 0 | return NULL; |
864 | 71.0k | } |
865 | | |
866 | | /**************************************************************************** |
867 | | * Packetize: just forwards raw blocks, or packetizes LOAS/ADTS |
868 | | * and strips headers |
869 | | ****************************************************************************/ |
870 | | static block_t *Packetize(decoder_t *p_dec, block_t **pp_block) |
871 | 204k | { |
872 | 204k | decoder_sys_t *p_sys = p_dec->p_sys; |
873 | 204k | block_t *p_block = pp_block ? *pp_block : NULL; |
874 | | |
875 | 204k | if(p_block) |
876 | 133k | { |
877 | 133k | if (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED)) |
878 | 4.15k | { |
879 | 4.15k | if(p_sys->i_type == TYPE_ADTS || p_sys->i_type == TYPE_LOAS) |
880 | 1.72k | { |
881 | | /* First always drain complete blocks before discontinuity */ |
882 | 1.72k | block_t *p_drain = PacketizeStreamBlock(p_dec, NULL); |
883 | 1.72k | if(p_drain) |
884 | 3 | return p_drain; |
885 | 1.72k | } |
886 | | |
887 | 4.15k | Flush(p_dec); |
888 | | |
889 | 4.15k | if (p_block->i_flags & BLOCK_FLAG_CORRUPTED) |
890 | 4.14k | { |
891 | 4.14k | block_Release(p_block); |
892 | 4.14k | return NULL; |
893 | 4.14k | } |
894 | 4.15k | } |
895 | | |
896 | 129k | if ( p_block->i_pts == VLC_TICK_INVALID && |
897 | 49.5k | date_Get(&p_sys->end_date) == VLC_TICK_INVALID ) |
898 | 4.05k | { |
899 | | /* We've just started the stream, wait for the first PTS. */ |
900 | 4.05k | block_Release(p_block); |
901 | 4.05k | return NULL; |
902 | 4.05k | } |
903 | 129k | } |
904 | | |
905 | 195k | if(p_block && p_sys->i_type == TYPE_UNKNOWN) |
906 | 3.53k | { |
907 | 3.53k | p_sys->i_type = TYPE_RAW; |
908 | 3.53k | if(p_block->i_buffer > 1) |
909 | 3.48k | { |
910 | 3.48k | if(p_block->p_buffer[0] == 0xff && (p_block->p_buffer[1] & 0xf6) == 0xf0) |
911 | 354 | { |
912 | 354 | p_sys->i_type = TYPE_ADTS; |
913 | 354 | } |
914 | 3.13k | else if(p_block->p_buffer[0] == 0x56 && (p_block->p_buffer[1] & 0xe0) == 0xe0) |
915 | 444 | { |
916 | 444 | p_sys->i_type = TYPE_LOAS; |
917 | 444 | } |
918 | 3.48k | } |
919 | 3.53k | } |
920 | | |
921 | 195k | if(p_sys->i_type == TYPE_RAW) |
922 | 126k | p_block = ForwardRawBlock(p_dec, pp_block); |
923 | 69.3k | else |
924 | 69.3k | p_block = PacketizeStreamBlock(p_dec, pp_block); |
925 | | |
926 | 195k | if(p_block && p_sys->b_discontuinity) |
927 | 7.25k | { |
928 | 7.25k | p_block->i_flags |= BLOCK_FLAG_DISCONTINUITY; |
929 | 7.25k | p_sys->b_discontuinity = false; |
930 | 7.25k | } |
931 | | |
932 | 195k | return p_block; |
933 | 204k | } |