/src/vlc/modules/packetizer/a52.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * a52.c: parse A/52 audio sync info and packetize the stream |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2001-2016 VLC authors and VideoLAN |
5 | | * |
6 | | * Authors: Stéphane Borel <stef@via.ecp.fr> |
7 | | * Christophe Massiot <massiot@via.ecp.fr> |
8 | | * Gildas Bazin <gbazin@videolan.org> |
9 | | * |
10 | | * This program is free software; you can redistribute it and/or modify it |
11 | | * under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation; either version 2.1 of the License, or |
13 | | * (at your option) any later version. |
14 | | * |
15 | | * This program is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public License |
21 | | * along with this program; if not, write to the Free Software Foundation, |
22 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
23 | | *****************************************************************************/ |
24 | | |
25 | | /***************************************************************************** |
26 | | * Preamble |
27 | | *****************************************************************************/ |
28 | | #ifdef HAVE_CONFIG_H |
29 | | # include "config.h" |
30 | | #endif |
31 | | |
32 | | #include <assert.h> |
33 | | |
34 | | #include <vlc_common.h> |
35 | | #include <vlc_plugin.h> |
36 | | #include <vlc_codec.h> |
37 | | #include <vlc_block_helper.h> |
38 | | #include <vlc_modules.h> |
39 | | |
40 | | #include "a52.h" |
41 | | |
42 | | #include "packetizer_helper.h" |
43 | | |
44 | | static int Open( vlc_object_t * ); |
45 | | static void Close( vlc_object_t * ); |
46 | | |
47 | 108 | vlc_module_begin () |
48 | 54 | set_subcategory(SUBCAT_SOUT_PACKETIZER) |
49 | 54 | set_description( N_("A/52 audio packetizer") ) |
50 | 54 | set_capability( "audio packetizer", 10 ) |
51 | 108 | set_callbacks( Open, Close ) |
52 | 54 | vlc_module_end () |
53 | | |
54 | | static const uint8_t startcode[2] = { 0x0b, 0x77 }; |
55 | | |
56 | | typedef struct |
57 | | { |
58 | | /* |
59 | | * Input properties |
60 | | */ |
61 | | int i_state; |
62 | | |
63 | | block_bytestream_t bytestream; |
64 | | |
65 | | /* |
66 | | * Common properties |
67 | | */ |
68 | | date_t end_date; |
69 | | vlc_tick_t i_prev_bytestream_pts; |
70 | | bool b_discontuinity; |
71 | | |
72 | | vlc_a52_header_t frame; |
73 | | size_t i_input_size; |
74 | | } decoder_sys_t; |
75 | | |
76 | | static void PacketizeFlush( decoder_t *p_dec ) |
77 | 0 | { |
78 | 0 | decoder_sys_t *p_sys = p_dec->p_sys; |
79 | |
|
80 | 0 | p_sys->b_discontuinity = true; |
81 | 0 | date_Set( &p_sys->end_date, VLC_TICK_INVALID ); |
82 | 0 | p_sys->i_state = STATE_NOSYNC; |
83 | 0 | p_sys->i_prev_bytestream_pts = VLC_TICK_INVALID; |
84 | 0 | block_BytestreamEmpty( &p_sys->bytestream ); |
85 | 0 | } |
86 | | |
87 | | static block_t *GetOutBuffer( decoder_t *p_dec ) |
88 | 746k | { |
89 | 746k | decoder_sys_t *p_sys = p_dec->p_sys; |
90 | | |
91 | 746k | assert( p_sys->frame.i_rate > 0 ); |
92 | | |
93 | 746k | block_t *p_block = block_Alloc( p_sys->i_input_size ); |
94 | 746k | if( p_block == NULL ) |
95 | 0 | return NULL; |
96 | | |
97 | 746k | if( p_dec->fmt_out.audio.i_rate != p_sys->frame.i_rate ) |
98 | 107k | { |
99 | 107k | msg_Dbg( p_dec, "A/52 channels:%d samplerate:%d bitrate:%d", |
100 | 107k | p_sys->frame.i_channels, p_sys->frame.i_rate, p_sys->frame.i_bitrate ); |
101 | 107k | if( p_sys->end_date.i_divider_num ) |
102 | 107k | date_Change( &p_sys->end_date, p_sys->frame.i_rate, 1 ); |
103 | 52 | else |
104 | 52 | date_Init( &p_sys->end_date, p_sys->frame.i_rate, 1 ); |
105 | 107k | } |
106 | | |
107 | 746k | block_t *p_bshead = p_sys->bytestream.p_block; |
108 | | /* Ensure we can recover if there's only dts set */ |
109 | 746k | if( date_Get( &p_sys->end_date ) == VLC_TICK_INVALID && p_bshead->i_pts == VLC_TICK_INVALID ) |
110 | 29.1k | p_bshead->i_pts = p_bshead->i_dts; |
111 | | |
112 | 746k | if( p_bshead->i_pts != VLC_TICK_INVALID && p_bshead->i_pts != date_Get( &p_sys->end_date ) ) |
113 | 3.10k | { |
114 | | /* Make sure we don't reuse the same pts twice |
115 | | * as A/52 in PES sends multiple times the same pts */ |
116 | 3.10k | if( p_sys->bytestream.p_block->i_pts != p_sys->i_prev_bytestream_pts ) |
117 | 801 | { |
118 | 801 | date_t tempdate = p_sys->end_date; |
119 | 801 | date_Increment( &tempdate, p_sys->frame.i_samples ); |
120 | 801 | vlc_tick_t samplesduration = date_Get( &tempdate ) - date_Get( &p_sys->end_date ); |
121 | 801 | if( llabs(p_sys->bytestream.p_block->i_pts - date_Get( &p_sys->end_date )) > samplesduration ) |
122 | 801 | date_Set( &p_sys->end_date, p_sys->bytestream.p_block->i_pts ); |
123 | 801 | p_sys->i_prev_bytestream_pts = p_sys->bytestream.p_block->i_pts; |
124 | 801 | p_sys->bytestream.p_block->i_pts = VLC_TICK_INVALID; |
125 | 801 | } |
126 | 3.10k | } |
127 | | |
128 | 746k | p_dec->fmt_out.audio.i_rate = p_sys->frame.i_rate; |
129 | 746k | p_dec->fmt_out.audio.i_channels = p_sys->frame.i_channels; |
130 | 746k | if( p_dec->fmt_out.audio.i_bytes_per_frame < p_sys->frame.i_size ) |
131 | 2.13k | p_dec->fmt_out.audio.i_bytes_per_frame = p_sys->frame.i_size; |
132 | 746k | p_dec->fmt_out.audio.i_frame_length = p_sys->frame.i_samples; |
133 | | |
134 | 746k | p_dec->fmt_out.audio.i_chan_mode = p_sys->frame.i_chan_mode; |
135 | 746k | p_dec->fmt_out.audio.i_physical_channels = p_sys->frame.i_channels_conf; |
136 | | |
137 | 746k | p_dec->fmt_out.i_bitrate = p_sys->frame.i_bitrate; |
138 | | |
139 | 746k | p_block->i_nb_samples = p_sys->frame.i_samples; |
140 | 746k | p_block->i_pts = p_block->i_dts = date_Get( &p_sys->end_date ); |
141 | 746k | if( p_block->i_pts != VLC_TICK_INVALID ) |
142 | 717k | p_block->i_length = date_Increment( &p_sys->end_date, |
143 | 717k | p_block->i_nb_samples ) - p_block->i_pts; |
144 | | |
145 | 746k | return p_block; |
146 | 746k | } |
147 | | |
148 | | static block_t *PacketizeBlock( decoder_t *p_dec, block_t **pp_block ) |
149 | 778k | { |
150 | 778k | decoder_sys_t *p_sys = p_dec->p_sys; |
151 | 778k | uint8_t p_header[VLC_A52_MIN_HEADER_SIZE]; |
152 | 778k | block_t *p_out_buffer; |
153 | | |
154 | 778k | block_t *p_block = pp_block ? *pp_block : NULL; |
155 | | |
156 | 778k | if( p_block ) |
157 | 777k | { |
158 | 777k | if( p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY | BLOCK_FLAG_CORRUPTED) ) |
159 | 0 | { |
160 | | /* First always drain complete blocks before discontinuity */ |
161 | 0 | block_t *p_drain = PacketizeBlock( p_dec, NULL ); |
162 | 0 | if(p_drain) |
163 | 0 | return p_drain; |
164 | | |
165 | 0 | PacketizeFlush( p_dec ); |
166 | |
|
167 | 0 | if( p_block->i_flags & BLOCK_FLAG_CORRUPTED ) |
168 | 0 | { |
169 | 0 | block_Release( p_block ); |
170 | 0 | return NULL; |
171 | 0 | } |
172 | 0 | } |
173 | | |
174 | 777k | block_BytestreamPush( &p_sys->bytestream, p_block ); |
175 | 777k | } |
176 | | |
177 | 3.71M | while( 1 ) |
178 | 3.71M | { |
179 | 3.71M | switch( p_sys->i_state ) |
180 | 3.71M | { |
181 | 2.94M | case STATE_NOSYNC: |
182 | 23.6M | while( block_PeekBytes( &p_sys->bytestream, p_header, sizeof(startcode) ) |
183 | 23.6M | == VLC_SUCCESS ) |
184 | 23.6M | { |
185 | 23.6M | if( !memcmp(p_header, startcode, sizeof(startcode)) ) |
186 | 2.93M | { |
187 | 2.93M | p_sys->i_state = STATE_SYNC; |
188 | 2.93M | break; |
189 | 2.93M | } |
190 | | |
191 | 20.7M | block_SkipByte( &p_sys->bytestream ); |
192 | 20.7M | } |
193 | 2.94M | if( p_sys->i_state != STATE_SYNC ) |
194 | 8.70k | { |
195 | 8.70k | block_BytestreamFlush( &p_sys->bytestream ); |
196 | | |
197 | | /* Need more data */ |
198 | 8.70k | return NULL; |
199 | 8.70k | } |
200 | | /* fallthrough */ |
201 | | |
202 | 2.93M | case STATE_SYNC: |
203 | 2.93M | p_sys->i_state = STATE_HEADER; |
204 | | /* fallthrough */ |
205 | | |
206 | 2.94M | case STATE_HEADER: |
207 | | /* Get A/52 frame header (VLC_A52_HEADER_SIZE bytes) */ |
208 | 2.94M | if( block_PeekBytes( &p_sys->bytestream, p_header, |
209 | 2.94M | VLC_A52_MIN_HEADER_SIZE ) != VLC_SUCCESS ) |
210 | 5.71k | { |
211 | | /* Need more data */ |
212 | 5.71k | return NULL; |
213 | 5.71k | } |
214 | | |
215 | | /* Check if frame is valid and get frame info */ |
216 | 2.93M | if( vlc_a52_header_Parse( &p_sys->frame, p_header, |
217 | 2.93M | VLC_A52_MIN_HEADER_SIZE ) != VLC_SUCCESS ) |
218 | 1.56M | { |
219 | 1.56M | msg_Dbg( p_dec, "emulated sync word" ); |
220 | 1.56M | block_SkipByte( &p_sys->bytestream ); |
221 | 1.56M | p_sys->i_state = STATE_NOSYNC; |
222 | 1.56M | break; |
223 | 1.56M | } |
224 | | |
225 | 1.37M | if( p_sys->frame.b_eac3 && p_sys->frame.bs.eac3.strmtyp == EAC3_STRMTYP_DEPENDENT ) |
226 | 624k | { |
227 | 624k | msg_Warn( p_dec, "starting with dependent stream, skip it" ); |
228 | 624k | p_sys->i_state = STATE_NOSYNC; |
229 | 624k | if( block_SkipBytes( &p_sys->bytestream, |
230 | 624k | p_sys->frame.i_size ) != VLC_SUCCESS ) |
231 | 3.56k | return NULL; |
232 | 620k | break; |
233 | 624k | } |
234 | | |
235 | 748k | p_sys->i_input_size = p_sys->frame.i_size; |
236 | 748k | p_sys->i_state = STATE_NEXT_SYNC; |
237 | | /* fallthrough */ |
238 | | |
239 | 761k | case STATE_NEXT_SYNC: |
240 | | /* Check if next expected frame contains the sync word */ |
241 | 761k | if( block_PeekOffsetBytes( &p_sys->bytestream, p_sys->i_input_size, |
242 | 761k | p_header, VLC_A52_MIN_HEADER_SIZE ) |
243 | 761k | != VLC_SUCCESS ) |
244 | 13.2k | { |
245 | 13.2k | if( p_block == NULL ) /* drain */ |
246 | 283 | { |
247 | 283 | p_sys->i_state = STATE_GET_DATA; |
248 | 283 | break; |
249 | 283 | } |
250 | | /* Need more data */ |
251 | 12.9k | return NULL; |
252 | 13.2k | } |
253 | | |
254 | 747k | if( p_header[0] == 0 || p_header[1] == 0 ) |
255 | 14.0k | { |
256 | | /* A52 wav files and audio CD's use stuffing */ |
257 | 14.0k | p_sys->i_state = STATE_GET_DATA; |
258 | 14.0k | break; |
259 | 14.0k | } |
260 | | |
261 | 733k | if( memcmp(p_header, startcode, sizeof(startcode)) ) |
262 | 544k | { |
263 | 544k | msg_Dbg( p_dec, "emulated sync word " |
264 | 544k | "(no sync on following frame)" ); |
265 | 544k | size_t offset = VLC_A52_MIN_HEADER_SIZE; |
266 | 544k | if( !block_FindStartcodeFromOffset( &p_sys->bytestream, &offset, |
267 | 544k | startcode, sizeof(startcode), |
268 | 544k | NULL, NULL ) ) |
269 | 543k | { |
270 | 543k | if( offset < p_sys->i_input_size ) |
271 | 312k | p_sys->i_input_size = offset; |
272 | | /* else will resync after read */ |
273 | 543k | } |
274 | 1.62k | else |
275 | 1.62k | { |
276 | 1.62k | p_sys->i_state = STATE_NOSYNC; |
277 | 1.62k | block_SkipByte( &p_sys->bytestream ); |
278 | 1.62k | break; |
279 | 1.62k | } |
280 | 544k | } |
281 | | |
282 | 732k | vlc_a52_header_t a52; |
283 | 732k | if( !vlc_a52_header_Parse( &a52, p_header, VLC_A52_MIN_HEADER_SIZE ) |
284 | 183k | && a52.b_eac3 && a52.bs.eac3.strmtyp == EAC3_STRMTYP_DEPENDENT ) |
285 | 7.64k | { |
286 | 7.64k | p_sys->i_input_size += a52.i_size; |
287 | 7.64k | p_dec->fmt_out.i_codec = VLC_CODEC_A52; |
288 | 7.64k | p_dec->fmt_out.i_profile = VLC_A52_PROFILE_EAC3_DEPENDENT; |
289 | 7.64k | } |
290 | | |
291 | 732k | p_sys->i_state = STATE_GET_DATA; |
292 | 732k | break; |
293 | | |
294 | 747k | case STATE_GET_DATA: |
295 | | /* Make sure we have enough data. */ |
296 | 747k | if( block_WaitBytes( &p_sys->bytestream, |
297 | 747k | p_sys->i_input_size ) != VLC_SUCCESS ) |
298 | 1.19k | { |
299 | | /* Need more data */ |
300 | 1.19k | return NULL; |
301 | 1.19k | } |
302 | 746k | p_sys->i_state = STATE_SEND_DATA; |
303 | | /* fallthrough */ |
304 | | |
305 | 746k | case STATE_SEND_DATA: |
306 | 746k | if( !(p_out_buffer = GetOutBuffer( p_dec )) ) |
307 | 0 | { |
308 | 0 | return NULL; |
309 | 0 | } |
310 | | |
311 | | /* Copy the whole frame into the buffer. When we reach this point |
312 | | * we already know we have enough data available. */ |
313 | 746k | block_GetBytes( &p_sys->bytestream, p_out_buffer->p_buffer, |
314 | 746k | p_out_buffer->i_buffer ); |
315 | | |
316 | 746k | p_sys->i_state = STATE_NOSYNC; |
317 | | |
318 | 746k | if( p_out_buffer->i_dts == VLC_TICK_INVALID ) |
319 | 29.1k | { |
320 | 29.1k | block_Release( p_out_buffer ); |
321 | 29.1k | return NULL; |
322 | 29.1k | } |
323 | | |
324 | 717k | if( p_sys->b_discontuinity ) |
325 | 0 | { |
326 | 0 | p_out_buffer->i_flags |= BLOCK_FLAG_DISCONTINUITY; |
327 | 0 | p_sys->b_discontuinity = false; |
328 | 0 | } |
329 | | |
330 | | /* So p_block doesn't get re-added several times */ |
331 | 717k | if( pp_block ) |
332 | 717k | *pp_block = block_BytestreamPop( &p_sys->bytestream ); |
333 | | |
334 | 717k | return p_out_buffer; |
335 | 3.71M | } |
336 | 3.71M | } |
337 | 778k | } |
338 | | |
339 | | static void Close( vlc_object_t *p_this ) |
340 | 2.18k | { |
341 | 2.18k | decoder_t *p_dec = (decoder_t*)p_this; |
342 | 2.18k | decoder_sys_t *p_sys = p_dec->p_sys; |
343 | | |
344 | 2.18k | block_BytestreamRelease( &p_sys->bytestream ); |
345 | | |
346 | 2.18k | free( p_sys ); |
347 | 2.18k | } |
348 | | |
349 | | static int Open( vlc_object_t *p_this ) |
350 | 3.07M | { |
351 | 3.07M | decoder_t *p_dec = (decoder_t*)p_this; |
352 | 3.07M | decoder_sys_t *p_sys; |
353 | | |
354 | 3.07M | switch( p_dec->fmt_in->i_codec ) |
355 | 3.07M | { |
356 | 1.25k | case VLC_CODEC_EAC3: |
357 | 2.18k | case VLC_CODEC_A52: |
358 | 2.18k | break; |
359 | 3.07M | default: |
360 | 3.07M | return VLC_EGENERIC; |
361 | 3.07M | } |
362 | | |
363 | | /* Allocate the memory needed to store the decoder's structure */ |
364 | 2.18k | if( ( p_dec->p_sys = p_sys = |
365 | 2.18k | (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL ) |
366 | 0 | return VLC_ENOMEM; |
367 | | |
368 | | /* Misc init */ |
369 | 2.18k | p_sys->i_state = STATE_NOSYNC; |
370 | 2.18k | date_Set( &p_sys->end_date, VLC_TICK_INVALID ); |
371 | 2.18k | p_sys->b_discontuinity = false; |
372 | 2.18k | p_sys->i_prev_bytestream_pts = VLC_TICK_INVALID; |
373 | 2.18k | memset(&p_sys->frame, 0, sizeof(vlc_a52_header_t)); |
374 | | |
375 | 2.18k | block_BytestreamInit( &p_sys->bytestream ); |
376 | | |
377 | | /* Set output properties (Passthrough ONLY) */ |
378 | 2.18k | p_dec->fmt_out.i_codec = p_dec->fmt_in->i_codec; |
379 | 2.18k | p_dec->fmt_out.audio = p_dec->fmt_in->audio; |
380 | 2.18k | p_dec->fmt_out.audio.i_rate = 0; |
381 | | |
382 | | /* Set callback */ |
383 | 2.18k | p_dec->pf_packetize = PacketizeBlock; |
384 | 2.18k | p_dec->pf_flush = PacketizeFlush; |
385 | 2.18k | p_dec->pf_get_cc = NULL; |
386 | 2.18k | return VLC_SUCCESS; |
387 | 2.18k | } |