/src/vlc/modules/demux/mpeg/ts_pes.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * ts_pes.c: Transport Stream input module for VLC. |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2004-2019 VLC authors and VideoLAN |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or modify it |
7 | | * under the terms of the GNU Lesser General Public License as published by |
8 | | * the Free Software Foundation; either version 2.1 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * This program is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public License |
17 | | * along with this program; if not, write to the Free Software Foundation, |
18 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
19 | | *****************************************************************************/ |
20 | | #ifdef HAVE_CONFIG_H |
21 | | # include "config.h" |
22 | | #endif |
23 | | |
24 | | #include <vlc_common.h> |
25 | | #include <vlc_demux.h> |
26 | | |
27 | | #include "ts_streams.h" |
28 | | #include "ts_pid.h" |
29 | | #include "ts_streams_private.h" |
30 | | |
31 | | #include "ts_pes.h" |
32 | | |
33 | | #include <assert.h> |
34 | | |
35 | | /* Avoids largest memcpy */ |
36 | | static bool block_Split( block_t **pp_block, block_t **pp_remain, size_t i_offset ) |
37 | 0 | { |
38 | 0 | block_t *p_block = *pp_block; |
39 | 0 | block_t *p_split = NULL; |
40 | 0 | *pp_remain = NULL; |
41 | |
|
42 | 0 | size_t i_tocopy = p_block->i_buffer - i_offset; |
43 | 0 | if( i_tocopy > i_offset ) /* make new block for head */ |
44 | 0 | { |
45 | 0 | if( i_offset > 0 ) |
46 | 0 | { |
47 | 0 | p_split = block_Alloc( i_offset ); |
48 | 0 | if( p_split == NULL ) |
49 | 0 | return false; |
50 | 0 | memcpy( p_split->p_buffer, p_block->p_buffer, i_offset ); |
51 | 0 | p_block->p_buffer += i_offset; |
52 | 0 | p_block->i_buffer -= i_offset; |
53 | 0 | } |
54 | 0 | *pp_remain = p_block; |
55 | 0 | *pp_block = p_split; |
56 | 0 | } |
57 | 0 | else /* other gets the tail of our split */ |
58 | 0 | { |
59 | 0 | if( i_tocopy > 0 ) |
60 | 0 | { |
61 | 0 | p_split = block_Alloc( i_tocopy ); |
62 | 0 | if( p_split == NULL ) |
63 | 0 | return false; |
64 | 0 | memcpy( p_split->p_buffer, &p_block->p_buffer[i_offset], i_tocopy ); |
65 | 0 | p_block->i_buffer -= i_tocopy; |
66 | 0 | } |
67 | 0 | *pp_remain = p_split; |
68 | 0 | } |
69 | 0 | return true; |
70 | 0 | } |
71 | | |
72 | | static const uint8_t pes_sync[] = { 0, 0, 1 }; |
73 | | |
74 | | static bool MayHaveStartCodeOnEnd( const uint8_t *p_buf, size_t i_buf ) |
75 | 0 | { |
76 | 0 | assert(i_buf > 2); |
77 | 0 | return !( *(--p_buf) > 1 || *(--p_buf) > 0 || *(--p_buf) > 0 ); |
78 | 0 | } |
79 | | |
80 | | static uint8_t *FindNextPESHeader( uint8_t *p_buf, size_t i_buffer ) |
81 | 0 | { |
82 | 0 | const uint8_t *p_end = &p_buf[i_buffer]; |
83 | 0 | unsigned i_bitflow = 0; |
84 | 0 | for( ; p_buf != p_end; p_buf++ ) |
85 | 0 | { |
86 | 0 | i_bitflow <<= 1; |
87 | 0 | if( !*p_buf ) |
88 | 0 | { |
89 | 0 | i_bitflow |= 1; |
90 | 0 | } |
91 | 0 | else if( *p_buf == 0x01 && (i_bitflow & 0x06) == 0x06 ) /* >= two zero prefixed 1 */ |
92 | 0 | { |
93 | 0 | return p_buf - 2; |
94 | 0 | } |
95 | 0 | } |
96 | 0 | return NULL; |
97 | 0 | } |
98 | | |
99 | | static bool ts_pes_Push( ts_pes_parse_callback *cb, |
100 | | ts_stream_t *p_pes, block_t *p_pkt, |
101 | | bool b_unit_start, ts_90khz_t i_append_pcr ) |
102 | 3.04M | { |
103 | 3.04M | bool b_ret = false; |
104 | | |
105 | 3.04M | if ( b_unit_start && p_pes->gather.p_data ) |
106 | 895k | { |
107 | 895k | block_t *p_datachain = p_pes->gather.p_data; |
108 | 895k | uint32_t i_flags = p_pes->gather.i_block_flags; |
109 | 895k | if( p_pes->gather.i_data_size && |
110 | 884k | p_pes->gather.i_gathered != p_pes->gather.i_data_size ) |
111 | 354k | { |
112 | | /* too early unit start resulting from packet loss */ |
113 | | /* or ending on a pkt not belonging to PES (%15 packets loss) */ |
114 | | /* But some encoders can't compute PES size right #28649 :/ */ |
115 | 354k | if( p_pes->gather.i_gathered < p_pes->gather.i_data_size || |
116 | 73.7k | p_pes->gather.i_gathered > p_pes->gather.i_data_size + 16 ) |
117 | 322k | i_flags |= BLOCK_FLAG_CORRUPTED; |
118 | 354k | } |
119 | | /* Flush the pes from pid */ |
120 | 895k | p_pes->gather.p_data = NULL; |
121 | 895k | p_pes->gather.i_data_size = 0; |
122 | 895k | p_pes->gather.i_gathered = 0; |
123 | 895k | p_pes->gather.i_block_flags = 0; |
124 | 895k | p_pes->gather.pp_last = &p_pes->gather.p_data; |
125 | 895k | cb->pf_parse( cb->p_obj, cb->priv, p_datachain, i_flags, p_pes->gather.i_append_pcr ); |
126 | 895k | b_ret = true; |
127 | 895k | } |
128 | | |
129 | 3.04M | if( b_unit_start ) |
130 | 2.41M | p_pes->gather.i_append_pcr = i_append_pcr; |
131 | | |
132 | 3.04M | if( p_pkt == NULL ) |
133 | 1.51M | return b_ret; |
134 | | |
135 | 1.53M | if( p_pkt->i_buffer == 0 ) |
136 | 429 | { |
137 | 429 | block_Release( p_pkt ); |
138 | 429 | return b_ret; |
139 | 429 | } |
140 | | |
141 | 1.53M | if( !b_unit_start && p_pes->gather.p_data == NULL ) |
142 | 98.1k | { |
143 | | /* msg_Dbg( p_demux, "broken packet" ); */ |
144 | 98.1k | block_Release( p_pkt ); |
145 | 98.1k | return b_ret; |
146 | 98.1k | } |
147 | | |
148 | 1.43M | block_ChainLastAppend( &p_pes->gather.pp_last, p_pkt ); |
149 | 1.43M | p_pes->gather.i_gathered += p_pkt->i_buffer; |
150 | | |
151 | 1.43M | if( p_pes->gather.i_data_size > 0 && |
152 | 1.41M | p_pes->gather.i_gathered >= p_pes->gather.i_data_size ) |
153 | 603k | { |
154 | | /* re-enter in Flush above */ |
155 | 603k | assert(p_pes->gather.p_data); |
156 | 603k | return ts_pes_Push( cb, p_pes, NULL, true, i_append_pcr ); |
157 | 603k | } |
158 | | |
159 | 832k | return b_ret; |
160 | 1.43M | } |
161 | | |
162 | | bool ts_pes_Drain( ts_pes_parse_callback *cb, ts_stream_t *p_pes ) |
163 | 0 | { |
164 | 0 | return ts_pes_Push( cb, p_pes, NULL, true, TS_90KHZ_INVALID ); |
165 | 0 | } |
166 | | |
167 | | bool ts_pes_Gather( ts_pes_parse_callback *cb, |
168 | | ts_stream_t *p_pes, block_t *p_pkt, |
169 | | bool b_unit_start, bool b_valid_scrambling, |
170 | | ts_90khz_t i_append_pcr ) |
171 | 1.57M | { |
172 | 1.57M | bool b_ret = false; |
173 | 1.57M | bool b_single_payload = b_unit_start; /* Single payload in case of unit start */ |
174 | 1.57M | bool b_aligned_ts_payload = true; |
175 | | |
176 | 1.57M | if( unlikely(p_pes->b_broken_PUSI_conformance) ) |
177 | 0 | { |
178 | | /* Stream does not conform to payload_unit_start flag |
179 | | * applied to PES packets (AdTech private_stream_1) */ |
180 | 0 | b_aligned_ts_payload = false; |
181 | 0 | b_single_payload = false; |
182 | |
|
183 | 0 | } |
184 | | |
185 | | /* Deal with explicit packet loss */ |
186 | 1.57M | if( p_pkt->i_flags & BLOCK_FLAG_PRIVATE_PACKET_LOSS ) |
187 | 556k | { |
188 | | /* Flag unfinished unit as corrupted */ |
189 | 556k | if ( p_pes->gather.i_gathered ) |
190 | 280k | p_pes->gather.i_block_flags |= BLOCK_FLAG_CORRUPTED; |
191 | 556k | p_pkt->i_flags &= ~BLOCK_FLAG_PRIVATE_PACKET_LOSS; |
192 | 556k | } |
193 | | |
194 | | /* We'll cannot parse any pes data */ |
195 | 1.57M | if( (p_pkt->i_flags & BLOCK_FLAG_SCRAMBLED) && b_valid_scrambling ) |
196 | 958 | { |
197 | 958 | block_Release( p_pkt ); |
198 | 958 | return ts_pes_Push( cb, p_pes, NULL, true, i_append_pcr ); |
199 | 958 | } |
200 | | |
201 | | /* Seek discontinuity, we need to drop or output currently |
202 | | * gathered data */ |
203 | 1.57M | if( p_pkt->i_flags & BLOCK_FLAG_PRIVATE_SOURCE_RANDOM_ACCESS ) |
204 | 2.01k | { |
205 | 2.01k | p_pes->gather.i_saved = 0; |
206 | | /* Flush/output current */ |
207 | 2.01k | b_ret |= ts_pes_Push( cb, p_pes, NULL, true, i_append_pcr ); |
208 | | /* Propagate to output block to notify packetizers/decoders */ |
209 | 2.01k | if( p_pes->p_es ) |
210 | 2.01k | p_pes->p_es->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY; |
211 | 2.01k | } |
212 | | /* On dropped packets, detected by continuity counter */ |
213 | 1.57M | else if( p_pkt->i_flags & BLOCK_FLAG_DISCONTINUITY ) |
214 | 0 | { |
215 | | /* If we know the final size and didn't gather enough bytes it is corrupted |
216 | | or if the discontinuity doesn't carry the start code */ |
217 | 0 | if( p_pes->gather.i_gathered && (p_pes->gather.i_data_size || |
218 | 0 | (b_aligned_ts_payload && !b_unit_start) ) ) |
219 | 0 | p_pes->gather.i_block_flags |= BLOCK_FLAG_CORRUPTED; |
220 | 0 | b_ret |= ts_pes_Push( cb, p_pes, NULL, true, i_append_pcr ); |
221 | | |
222 | | /* it can't match the target size and need to resync on sync code */ |
223 | 0 | p_pes->gather.i_data_size = 0; |
224 | | /* can't reuse prev bytes to lookup sync code */ |
225 | 0 | p_pes->gather.i_saved = 0; |
226 | | /* Propagate to output block to notify packetizers/decoders */ |
227 | 0 | if( p_pes->p_es ) |
228 | 0 | p_pes->p_es->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY; |
229 | 0 | p_pes->gather.i_block_flags|= BLOCK_FLAG_DISCONTINUITY; |
230 | 0 | } |
231 | | /* On dropped packets, detected by continuity counter */ |
232 | 1.57M | else if( p_pkt->i_flags & BLOCK_FLAG_CORRUPTED ) |
233 | 0 | { |
234 | 0 | p_pes->gather.i_block_flags |= BLOCK_FLAG_CORRUPTED; |
235 | | /* can't reuse prev bytes to lookup sync code */ |
236 | 0 | p_pes->gather.i_saved = 0; |
237 | 0 | } |
238 | | |
239 | 1.57M | if ( unlikely(p_pes->gather.i_saved > 0) ) |
240 | 0 | { |
241 | | /* Saved from previous packet end */ |
242 | 0 | assert(p_pes->gather.i_saved < TS_PES_HEADER_SIZE); |
243 | 0 | if( !b_aligned_ts_payload ) |
244 | 0 | { |
245 | 0 | p_pkt = block_Realloc( p_pkt, p_pes->gather.i_saved, p_pkt->i_buffer ); |
246 | 0 | if( p_pkt ) |
247 | 0 | memcpy( p_pkt->p_buffer, p_pes->gather.saved, p_pes->gather.i_saved ); |
248 | 0 | } |
249 | 0 | p_pes->gather.i_saved = 0; |
250 | 0 | } |
251 | | |
252 | 3.72M | for( bool b_first_sync_done = false; p_pkt; ) |
253 | 2.19M | { |
254 | 2.19M | assert( p_pes->gather.i_saved == 0 ); |
255 | | |
256 | 2.19M | if( p_pes->gather.p_data == NULL && b_unit_start && !b_first_sync_done && p_pkt->i_buffer >= TS_PES_HEADER_SIZE ) |
257 | 657k | { |
258 | 657k | if( likely(b_aligned_ts_payload) ) |
259 | 657k | { |
260 | 657k | if( memcmp( p_pkt->p_buffer, pes_sync, 3 ) ) |
261 | 44.5k | { |
262 | 44.5k | block_Release( p_pkt ); |
263 | 44.5k | return b_ret; |
264 | 44.5k | } |
265 | 657k | } |
266 | 0 | else |
267 | 0 | { |
268 | | /* Need to find sync code */ |
269 | 0 | uint8_t *p_buf = FindNextPESHeader( p_pkt->p_buffer, p_pkt->i_buffer - 3 ); |
270 | 0 | if( p_buf == NULL ) |
271 | 0 | { |
272 | | /* no first sync code */ |
273 | 0 | if( MayHaveStartCodeOnEnd( p_pkt->p_buffer, p_pkt->i_buffer ) ) |
274 | 0 | { |
275 | | /* Drop everything except last bytes for next packet */ |
276 | 0 | p_pkt->p_buffer += p_pkt->i_buffer - 3; |
277 | 0 | p_pes->gather.i_saved = p_pkt->i_buffer = 3; |
278 | 0 | memcpy(p_pes->gather.saved, p_pkt->p_buffer, p_pkt->i_buffer); |
279 | 0 | } |
280 | 0 | block_Release( p_pkt ); |
281 | 0 | return b_ret; |
282 | 0 | } |
283 | 0 | p_pkt->i_buffer -= p_buf - p_pkt->p_buffer; |
284 | 0 | p_pkt->p_buffer = p_buf; |
285 | 0 | } |
286 | | /* now points to PES header */ |
287 | 612k | p_pes->gather.i_data_size = GetWBE(&p_pkt->p_buffer[4]); |
288 | 612k | if( p_pes->gather.i_data_size > 0 ) |
289 | 607k | p_pes->gather.i_data_size += TS_PES_HEADER_SIZE; |
290 | 612k | b_first_sync_done = true; /* Because if size is 0, we would not look for second sync */ |
291 | 612k | } |
292 | 1.53M | else |
293 | 1.53M | { |
294 | 1.53M | assert( p_pes->gather.i_data_size > p_pes->gather.i_gathered || |
295 | 1.53M | p_pes->gather.i_data_size == 0 ); |
296 | | |
297 | | /* If we started reading a fixed size that might not end on boundary */ |
298 | 1.53M | if( unlikely(!b_aligned_ts_payload) && |
299 | 0 | p_pes->gather.i_data_size > p_pes->gather.i_gathered ) |
300 | 0 | { |
301 | 0 | const size_t i_remain = p_pes->gather.i_data_size - p_pes->gather.i_gathered; |
302 | | /* Append whole block */ |
303 | 0 | if( likely(p_pkt->i_buffer <= i_remain) ) |
304 | 0 | { |
305 | 0 | b_ret |= ts_pes_Push( cb, p_pes, p_pkt, p_pes->gather.p_data == NULL, i_append_pcr ); |
306 | 0 | p_pkt = NULL; |
307 | 0 | } |
308 | 0 | else /* p_pkt->i_buffer > i_remain */ |
309 | 0 | { |
310 | 0 | block_t *p_split; |
311 | 0 | if( !block_Split( &p_pkt, &p_split, i_remain ) ) |
312 | 0 | { |
313 | 0 | block_Release( p_pkt ); |
314 | 0 | return false; |
315 | 0 | } |
316 | 0 | p_pes->gather.i_block_flags |= BLOCK_FLAG_CORRUPTED; |
317 | 0 | b_ret |= ts_pes_Push( cb, p_pes, p_pkt, p_pes->gather.p_data == NULL, i_append_pcr ); |
318 | 0 | p_pkt = p_split; |
319 | 0 | b_first_sync_done = false; |
320 | 0 | } |
321 | 0 | } |
322 | 1.53M | else /* if( p_pes->gather.i_data_size == 0 ) // see next packet */ |
323 | 1.53M | { |
324 | 1.53M | if( likely(b_aligned_ts_payload) && b_unit_start ) |
325 | 905k | { |
326 | 905k | b_ret |= ts_pes_Push( cb, p_pes, NULL, true, i_append_pcr ); |
327 | | /* now points to PES header */ |
328 | 905k | if( p_pkt->i_buffer >= TS_PES_HEADER_SIZE ) |
329 | 902k | { |
330 | 902k | p_pes->gather.i_data_size = GetWBE(&p_pkt->p_buffer[4]); |
331 | 902k | if( p_pes->gather.i_data_size > 0 ) |
332 | 893k | p_pes->gather.i_data_size += TS_PES_HEADER_SIZE; |
333 | 902k | } |
334 | 905k | } |
335 | | /* Append or finish current/start new PES depending on unit_start */ |
336 | 1.53M | b_ret |= ts_pes_Push( cb, p_pes, p_pkt, b_unit_start, i_append_pcr ); |
337 | 1.53M | p_pkt = NULL; |
338 | 1.53M | } |
339 | 1.53M | } |
340 | | |
341 | 2.14M | if( unlikely(p_pkt && p_pkt->i_buffer < TS_PES_HEADER_SIZE) ) |
342 | 0 | { |
343 | | /* save and prepend to next packet */ |
344 | 0 | assert(!b_single_payload); |
345 | 0 | assert(p_pes->gather.i_saved == 0); |
346 | 0 | p_pes->gather.i_saved = p_pkt->i_buffer; |
347 | 0 | memcpy(p_pes->gather.saved, p_pkt->p_buffer, p_pkt->i_buffer); |
348 | 0 | block_Release( p_pkt ); |
349 | 0 | p_pkt = NULL; |
350 | 0 | } |
351 | 2.14M | } |
352 | | |
353 | 1.53M | return b_ret; |
354 | 1.57M | } |