FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/ty.c
Date: 2026-09-25 23:13:28
Exec Total Coverage
Lines: 4 334 1.2%
Functions: 1 11 9.1%
Branches: 3 201 1.5%

Line Branch Exec Source
1 /*
2 * TiVo ty stream demuxer
3 * Copyright (c) 2005 VLC authors and VideoLAN
4 * Copyright (c) 2005 by Neal Symms (tivo@freakinzoo.com) - February 2005
5 * based on code by Christopher Wingert for tivo-mplayer
6 * tivo(at)wingert.org, February 2003
7 * Copyright (c) 2017 Paul B Mahol
8 *
9 * This file is part of FFmpeg.
10 *
11 * FFmpeg is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
15 *
16 * FFmpeg is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with FFmpeg; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 */
25
26 #include "libavutil/attributes.h"
27 #include "libavutil/intreadwrite.h"
28 #include "libavutil/mem.h"
29 #include "avformat.h"
30 #include "demux.h"
31 #include "internal.h"
32 #include "mpeg.h"
33
34 #define SERIES1_PES_LENGTH 11 /* length of audio PES hdr on S1 */
35 #define SERIES2_PES_LENGTH 16 /* length of audio PES hdr on S2 */
36 #define AC3_PES_LENGTH 14 /* length of audio PES hdr for AC3 */
37 #define VIDEO_PES_LENGTH 16 /* length of video PES header */
38 #define DTIVO_PTS_OFFSET 6 /* offs into PES for MPEG PTS on DTivo */
39 #define SA_PTS_OFFSET 9 /* offset into PES for MPEG PTS on SA */
40 #define AC3_PTS_OFFSET 9 /* offset into PES for AC3 PTS on DTivo */
41 #define VIDEO_PTS_OFFSET 9 /* offset into PES for video PTS on all */
42 #define AC3_PKT_LENGTH 1536 /* size of TiVo AC3 pkts (w/o PES hdr) */
43
44 static const uint8_t ty_VideoPacket[] = { 0x00, 0x00, 0x01, 0xe0 };
45 static const uint8_t ty_MPEGAudioPacket[] = { 0x00, 0x00, 0x01, 0xc0 };
46 static const uint8_t ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd };
47
48 #define TIVO_PES_FILEID 0xf5467abd
49 #define CHUNK_SIZE (128 * 1024)
50 #define CHUNK_PEEK_COUNT 3 /* number of chunks to probe */
51
52 typedef struct TyRecHdr {
53 int32_t rec_size;
54 uint8_t ex[2];
55 uint8_t rec_type;
56 uint8_t subrec_type;
57 uint64_t ty_pts; /* TY PTS in the record header */
58 } TyRecHdr;
59
60 typedef enum {
61 TIVO_TYPE_UNKNOWN,
62 TIVO_TYPE_SA,
63 TIVO_TYPE_DTIVO
64 } TiVo_type;
65
66 typedef enum {
67 TIVO_SERIES_UNKNOWN,
68 TIVO_SERIES1,
69 TIVO_SERIES2
70 } TiVo_series;
71
72 typedef enum {
73 TIVO_AUDIO_UNKNOWN,
74 TIVO_AUDIO_AC3,
75 TIVO_AUDIO_MPEG
76 } TiVo_audio;
77
78 typedef struct TYDemuxContext {
79 unsigned cur_chunk;
80 unsigned cur_chunk_pos;
81 int64_t cur_pos;
82 TiVo_type tivo_type; /* TiVo type (SA / DTiVo) */
83 TiVo_series tivo_series; /* Series1 or Series2 */
84 TiVo_audio audio_type; /* AC3 or MPEG */
85 int pes_length; /* Length of Audio PES header */
86 int pts_offset; /* offset into audio PES of PTS */
87 uint8_t pes_buffer[20]; /* holds incomplete pes headers */
88 int pes_buf_cnt; /* how many bytes in our buffer */
89 size_t ac3_pkt_size; /* length of ac3 pkt we've seen so far */
90 uint64_t last_ty_pts; /* last TY timestamp we've seen */
91
92 int64_t first_audio_pts;
93 int64_t last_audio_pts;
94 int64_t last_video_pts;
95
96 TyRecHdr *rec_hdrs; /* record headers array */
97 int cur_rec; /* current record in this chunk */
98 int num_recs; /* number of recs in this chunk */
99 int first_chunk;
100
101 uint8_t chunk[CHUNK_SIZE];
102 } TYDemuxContext;
103
104 8066 static int ty_probe(const AVProbeData *p)
105 {
106 int i;
107
108
2/2
✓ Branch 0 taken 10188 times.
✓ Branch 1 taken 8066 times.
18254 for (i = 0; i + 12 < p->buf_size; i += CHUNK_SIZE) {
109
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10188 times.
10188 if (AV_RB32(p->buf + i) == TIVO_PES_FILEID &&
110 ✗ AV_RB32(p->buf + i + 4) == 0x02 &&
111 ✗ AV_RB32(p->buf + i + 8) == CHUNK_SIZE) {
112 ✗ return AVPROBE_SCORE_MAX;
113 }
114 }
115
116 8066 return 0;
117 }
118
119 ✗ static TyRecHdr *parse_chunk_headers(const uint8_t *buf,
120 int num_recs)
121 {
122 TyRecHdr *hdrs, *rec_hdr;
123 int i;
124
125 ✗ hdrs = av_calloc(num_recs, sizeof(TyRecHdr));
126 ✗ if (!hdrs)
127 ✗ return NULL;
128
129 ✗ for (i = 0; i < num_recs; i++) {
130 ✗ const uint8_t *record_header = buf + (i * 16);
131
132 ✗ rec_hdr = &hdrs[i]; /* for brevity */
133 ✗ rec_hdr->rec_type = record_header[3];
134 ✗ rec_hdr->subrec_type = record_header[2] & 0x0f;
135 ✗ if ((record_header[0] & 0x80) == 0x80) {
136 uint8_t b1, b2;
137
138 /* marker bit 2 set, so read extended data */
139 ✗ b1 = (((record_header[0] & 0x0f) << 4) |
140 ✗ ((record_header[1] & 0xf0) >> 4));
141 ✗ b2 = (((record_header[1] & 0x0f) << 4) |
142 ✗ ((record_header[2] & 0xf0) >> 4));
143
144 ✗ rec_hdr->ex[0] = b1;
145 ✗ rec_hdr->ex[1] = b2;
146 ✗ rec_hdr->rec_size = 0;
147 ✗ rec_hdr->ty_pts = 0;
148 } else {
149 ✗ rec_hdr->rec_size = (record_header[0] << 8 |
150 ✗ record_header[1]) << 4 |
151 ✗ (record_header[2] >> 4);
152 ✗ rec_hdr->ty_pts = AV_RB64(&record_header[8]);
153 }
154 }
155 ✗ return hdrs;
156 }
157
158 ✗ static int find_es_header(const uint8_t *header,
159 const uint8_t *buffer, int search_len)
160 {
161 int count;
162
163 ✗ for (count = 0; count < search_len; count++) {
164 ✗ if (!memcmp(&buffer[count], header, 4))
165 ✗ return count;
166 }
167 ✗ return -1;
168 }
169
170 ✗ static int analyze_chunk(AVFormatContext *s, const uint8_t *chunk)
171 {
172 ✗ TYDemuxContext *ty = s->priv_data;
173 int num_recs, i;
174 TyRecHdr *hdrs;
175 int num_6e0, num_be0, num_9c0, num_3c0;
176
177 /* skip if it's a Part header */
178 ✗ if (AV_RB32(&chunk[0]) == TIVO_PES_FILEID)
179 ✗ return 0;
180
181 /* number of records in chunk (we ignore high order byte;
182 * rarely are there > 256 chunks & we don't need that many anyway) */
183 ✗ num_recs = chunk[0];
184 ✗ if (num_recs < 5) {
185 /* try again with the next chunk. Sometimes there are dead ones */
186 ✗ return 0;
187 }
188
189 ✗ chunk += 4; /* skip past rec count & SEQ bytes */
190 ff_dlog(s, "probe: chunk has %d recs\n", num_recs);
191 ✗ hdrs = parse_chunk_headers(chunk, num_recs);
192 ✗ if (!hdrs)
193 ✗ return AVERROR(ENOMEM);
194
195 /* scan headers.
196 * 1. check video packets. Presence of 0x6e0 means S1.
197 * No 6e0 but have be0 means S2.
198 * 2. probe for audio 0x9c0 vs 0x3c0 (AC3 vs Mpeg)
199 * If AC-3, then we have DTivo.
200 * If MPEG, search for PTS offset. This will determine SA vs. DTivo.
201 */
202 ✗ num_6e0 = num_be0 = num_9c0 = num_3c0 = 0;
203 ✗ for (i = 0; i < num_recs; i++) {
204 ✗ switch (hdrs[i].subrec_type << 8 | hdrs[i].rec_type) {
205 ✗ case 0x6e0:
206 ✗ num_6e0++;
207 ✗ break;
208 ✗ case 0xbe0:
209 ✗ num_be0++;
210 ✗ break;
211 ✗ case 0x3c0:
212 ✗ num_3c0++;
213 ✗ break;
214 ✗ case 0x9c0:
215 ✗ num_9c0++;
216 ✗ break;
217 }
218 }
219 ff_dlog(s, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.\n",
220 num_6e0, num_be0);
221
222 /* set up our variables */
223 ✗ if (num_6e0 > 0) {
224 ff_dlog(s, "detected Series 1 Tivo\n");
225 ✗ ty->tivo_series = TIVO_SERIES1;
226 ✗ ty->pes_length = SERIES1_PES_LENGTH;
227 ✗ } else if (num_be0 > 0) {
228 ff_dlog(s, "detected Series 2 Tivo\n");
229 ✗ ty->tivo_series = TIVO_SERIES2;
230 ✗ ty->pes_length = SERIES2_PES_LENGTH;
231 }
232 ✗ if (num_9c0 > 0) {
233 ff_dlog(s, "detected AC-3 Audio (DTivo)\n");
234 ✗ ty->audio_type = TIVO_AUDIO_AC3;
235 ✗ ty->tivo_type = TIVO_TYPE_DTIVO;
236 ✗ ty->pts_offset = AC3_PTS_OFFSET;
237 ✗ ty->pes_length = AC3_PES_LENGTH;
238 ✗ } else if (num_3c0 > 0) {
239 ✗ ty->audio_type = TIVO_AUDIO_MPEG;
240 ff_dlog(s, "detected MPEG Audio\n");
241 }
242
243 /* if tivo_type still unknown, we can check PTS location
244 * in MPEG packets to determine tivo_type */
245 ✗ if (ty->tivo_type == TIVO_TYPE_UNKNOWN) {
246 ✗ uint32_t data_offset = 16 * num_recs;
247
248 ✗ for (i = 0; i < num_recs; i++) {
249 ✗ if (data_offset + hdrs[i].rec_size > CHUNK_SIZE)
250 ✗ break;
251
252 ✗ if ((hdrs[i].subrec_type << 8 | hdrs[i].rec_type) == 0x3c0 && hdrs[i].rec_size > 15) {
253 /* first make sure we're aligned */
254 ✗ int pes_offset = find_es_header(ty_MPEGAudioPacket,
255 &chunk[data_offset], 5);
256 ✗ if (pes_offset >= 0) {
257 /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
258 ✗ if ((chunk[data_offset + 6 + pes_offset] & 0x80) == 0x80) {
259 /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
260 ✗ if (ty->tivo_series == TIVO_SERIES1)
261 ff_dlog(s, "detected Stand-Alone Tivo\n");
262 ✗ ty->tivo_type = TIVO_TYPE_SA;
263 ✗ ty->pts_offset = SA_PTS_OFFSET;
264 } else {
265 ✗ if (ty->tivo_series == TIVO_SERIES1)
266 ff_dlog(s, "detected DirecTV Tivo\n");
267 ✗ ty->tivo_type = TIVO_TYPE_DTIVO;
268 ✗ ty->pts_offset = DTIVO_PTS_OFFSET;
269 }
270 ✗ break;
271 }
272 }
273 ✗ data_offset += hdrs[i].rec_size;
274 }
275 }
276 ✗ av_free(hdrs);
277
278 ✗ return 0;
279 }
280
281 ✗ static int ty_read_header(AVFormatContext *s)
282 {
283 ✗ TYDemuxContext *ty = s->priv_data;
284 ✗ AVIOContext *pb = s->pb;
285 AVStream *st, *ast;
286 ✗ int i, ret = 0;
287
288 ✗ ty->first_audio_pts = AV_NOPTS_VALUE;
289 ✗ ty->last_audio_pts = AV_NOPTS_VALUE;
290 ✗ ty->last_video_pts = AV_NOPTS_VALUE;
291
292 ✗ for (i = 0; i < CHUNK_PEEK_COUNT; i++) {
293 ✗ avio_read(pb, ty->chunk, CHUNK_SIZE);
294
295 ✗ ret = analyze_chunk(s, ty->chunk);
296 ✗ if (ret < 0)
297 ✗ return ret;
298 ✗ if (ty->tivo_series != TIVO_SERIES_UNKNOWN &&
299 ✗ ty->audio_type != TIVO_AUDIO_UNKNOWN &&
300 ✗ ty->tivo_type != TIVO_TYPE_UNKNOWN)
301 ✗ break;
302 }
303
304 ✗ if (ty->tivo_series == TIVO_SERIES_UNKNOWN ||
305 ✗ ty->audio_type == TIVO_AUDIO_UNKNOWN ||
306 ✗ ty->tivo_type == TIVO_TYPE_UNKNOWN)
307 ✗ return AVERROR_INVALIDDATA;
308
309 ✗ st = avformat_new_stream(s, NULL);
310 ✗ if (!st)
311 ✗ return AVERROR(ENOMEM);
312 ✗ st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
313 ✗ st->codecpar->codec_id = AV_CODEC_ID_MPEG2VIDEO;
314 ✗ ffstream(st)->need_parsing = AVSTREAM_PARSE_FULL_RAW;
315 ✗ avpriv_set_pts_info(st, 64, 1, 90000);
316
317 ✗ ast = avformat_new_stream(s, NULL);
318 ✗ if (!ast)
319 ✗ return AVERROR(ENOMEM);
320 ✗ ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
321
322 ✗ if (ty->audio_type == TIVO_AUDIO_MPEG) {
323 ✗ ast->codecpar->codec_id = AV_CODEC_ID_MP2;
324 ✗ ffstream(ast)->need_parsing = AVSTREAM_PARSE_FULL_RAW;
325 } else {
326 ✗ ast->codecpar->codec_id = AV_CODEC_ID_AC3;
327 }
328 ✗ avpriv_set_pts_info(ast, 64, 1, 90000);
329
330 ✗ ty->first_chunk = 1;
331
332 ✗ avio_seek(pb, 0, SEEK_SET);
333
334 ✗ return 0;
335 }
336
337 ✗ static int get_chunk(AVFormatContext *s)
338 {
339 ✗ TYDemuxContext *ty = s->priv_data;
340 ✗ AVIOContext *pb = s->pb;
341 int read_size, num_recs;
342
343 ff_dlog(s, "parsing ty chunk #%d\n", ty->cur_chunk);
344
345 /* if we have left-over filler space from the last chunk, get that */
346 ✗ if (avio_feof(pb))
347 ✗ return AVERROR_EOF;
348
349 /* read the TY packet header */
350 ✗ read_size = avio_read(pb, ty->chunk, CHUNK_SIZE);
351 ✗ ty->cur_chunk++;
352
353 ✗ if ((read_size < 4) || (AV_RB32(ty->chunk) == 0)) {
354 ✗ return AVERROR_EOF;
355 }
356
357 /* check if it's a PART Header */
358 ✗ if (AV_RB32(ty->chunk) == TIVO_PES_FILEID) {
359 /* skip master chunk and read new chunk */
360 ✗ return get_chunk(s);
361 }
362
363 /* number of records in chunk (8- or 16-bit number) */
364 ✗ if (ty->chunk[3] & 0x80) {
365 /* 16 bit rec cnt */
366 ✗ ty->num_recs = num_recs = (ty->chunk[1] << 8) + ty->chunk[0];
367 } else {
368 /* 8 bit reclen - TiVo 1.3 format */
369 ✗ ty->num_recs = num_recs = ty->chunk[0];
370 }
371 ✗ ty->cur_rec = 0;
372 ✗ ty->first_chunk = 0;
373
374 ff_dlog(s, "chunk has %d records\n", num_recs);
375 ✗ ty->cur_chunk_pos = 4;
376
377 ✗ av_freep(&ty->rec_hdrs);
378
379 ✗ if (num_recs * 16 >= CHUNK_SIZE - 4)
380 ✗ return AVERROR_INVALIDDATA;
381
382 ✗ ty->rec_hdrs = parse_chunk_headers(ty->chunk + 4, num_recs);
383 ✗ if (!ty->rec_hdrs)
384 ✗ return AVERROR(ENOMEM);
385 ✗ ty->cur_chunk_pos += 16 * num_recs;
386
387 ✗ return 0;
388 }
389
390 ✗ static int demux_video(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt)
391 {
392 ✗ TYDemuxContext *ty = s->priv_data;
393 ✗ const int subrec_type = rec_hdr->subrec_type;
394 ✗ const int64_t rec_size = rec_hdr->rec_size;
395 int es_offset1, ret;
396 ✗ int got_packet = 0;
397
398 ✗ if (subrec_type != 0x02 && subrec_type != 0x0c &&
399 ✗ subrec_type != 0x08 && rec_size > 7) {
400
401 /* get the PTS from this packet if it has one.
402 * on S1, only 0x06 has PES. On S2, however, most all do.
403 * Do NOT Pass the PES Header to the MPEG2 codec */
404 ✗ es_offset1 = find_es_header(ty_VideoPacket, ty->chunk + ty->cur_chunk_pos, 5);
405 ✗ if (es_offset1 != -1) {
406 ✗ if (rec_size < es_offset1 + VIDEO_PTS_OFFSET + 5)
407 ✗ return AVERROR_INVALIDDATA;
408
409 ✗ ty->last_video_pts = ff_parse_pes_pts(
410 ✗ ty->chunk + ty->cur_chunk_pos + es_offset1 + VIDEO_PTS_OFFSET);
411 ✗ if (subrec_type != 0x06) {
412 /* if we found a PES, and it's not type 6, then we're S2 */
413 /* The packet will have video data (& other headers) so we
414 * chop out the PES header and send the rest */
415 ✗ if (rec_size >= VIDEO_PES_LENGTH + es_offset1) {
416 ✗ int size = rec_hdr->rec_size - VIDEO_PES_LENGTH - es_offset1;
417
418 ✗ ty->cur_chunk_pos += VIDEO_PES_LENGTH + es_offset1;
419 ✗ if ((ret = av_new_packet(pkt, size)) < 0)
420 ✗ return ret;
421 ✗ memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, size);
422 ✗ ty->cur_chunk_pos += size;
423 ✗ pkt->stream_index = 0;
424 ✗ got_packet = 1;
425 } else {
426 ff_dlog(s, "video rec type 0x%02x has short PES"
427 " (%"PRId64" bytes)\n", subrec_type, rec_size);
428 /* nuke this block; it's too short, but has PES marker */
429 ✗ ty->cur_chunk_pos += rec_size;
430 ✗ return 0;
431 }
432 }
433 }
434 }
435
436 ✗ if (subrec_type == 0x06) {
437 /* type 6 (S1 DTivo) has no data, so we're done */
438 ✗ ty->cur_chunk_pos += rec_size;
439 ✗ return 0;
440 }
441
442 ✗ if (!got_packet) {
443 ✗ if ((ret = av_new_packet(pkt, rec_size)) < 0)
444 ✗ return ret;
445 ✗ memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
446 ✗ ty->cur_chunk_pos += rec_size;
447 ✗ pkt->stream_index = 0;
448 ✗ got_packet = 1;
449 }
450
451 /* if it's not a continue blk, then set PTS */
452 ✗ if (subrec_type != 0x02) {
453 ✗ if (subrec_type == 0x0c && pkt->size >= 6)
454 ✗ pkt->data[5] |= 0x08;
455 ✗ if (subrec_type == 0x07) {
456 ✗ ty->last_ty_pts = rec_hdr->ty_pts;
457 } else {
458 /* yes I know this is a cheap hack. It's the timestamp
459 used for display and skipping fwd/back, so it
460 doesn't have to be accurate to the millisecond.
461 I adjust it here by roughly one 1/30 sec. Yes it
462 will be slightly off for UK streams, but it's OK.
463 */
464 ✗ ty->last_ty_pts += 35000000;
465 //ty->last_ty_pts += 33366667;
466 }
467 /* set PTS for this block before we send */
468 ✗ if (ty->last_video_pts > AV_NOPTS_VALUE) {
469 ✗ pkt->pts = ty->last_video_pts;
470 /* PTS gets used ONCE.
471 * Any subsequent frames we get BEFORE next PES
472 * header will have their PTS computed in the codec */
473 ✗ ty->last_video_pts = AV_NOPTS_VALUE;
474 }
475 }
476
477 ✗ return got_packet;
478 }
479
480 ✗ static int check_sync_pes(AVFormatContext *s, AVPacket *pkt,
481 int32_t offset, int32_t rec_len)
482 {
483 ✗ TYDemuxContext *ty = s->priv_data;
484
485 ✗ if (offset < 0 || offset + ty->pes_length > rec_len) {
486 /* entire PES header not present */
487 ff_dlog(s, "PES header at %"PRId32" not complete in record. storing.\n", offset);
488 /* save the partial pes header */
489 ✗ if (offset < 0) {
490 /* no header found, fake some 00's (this works, believe me) */
491 ✗ memset(ty->pes_buffer, 0, 4);
492 ✗ ty->pes_buf_cnt = 4;
493 if (rec_len > 4)
494 ff_dlog(s, "PES header not found in record of %"PRId32" bytes!\n", rec_len);
495 ✗ return -1;
496 }
497 /* copy the partial pes header we found */
498 ✗ memcpy(ty->pes_buffer, pkt->data + offset, rec_len - offset);
499 ✗ ty->pes_buf_cnt = rec_len - offset;
500
501 ✗ if (offset > 0) {
502 /* PES Header was found, but not complete, so trim the end of this record */
503 ✗ pkt->size -= rec_len - offset;
504 ✗ return 1;
505 }
506 ✗ return -1; /* partial PES, no audio data */
507 }
508 /* full PES header present, extract PTS */
509 ✗ ty->last_audio_pts = ff_parse_pes_pts(&pkt->data[ offset + ty->pts_offset]);
510 ✗ if (ty->first_audio_pts == AV_NOPTS_VALUE)
511 ✗ ty->first_audio_pts = ty->last_audio_pts;
512 ✗ pkt->pts = ty->last_audio_pts;
513 ✗ memmove(pkt->data + offset, pkt->data + offset + ty->pes_length, rec_len - ty->pes_length);
514 ✗ pkt->size -= ty->pes_length;
515 ✗ return 0;
516 }
517
518 ✗ static int demux_audio(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt)
519 {
520 ✗ TYDemuxContext *ty = s->priv_data;
521 ✗ const int subrec_type = rec_hdr->subrec_type;
522 ✗ const int64_t rec_size = rec_hdr->rec_size;
523 int es_offset1, ret;
524
525 ✗ if (subrec_type == 2) {
526 ✗ int need = 0;
527 /* SA or DTiVo Audio Data, no PES (continued block)
528 * ================================================
529 */
530
531 /* continue PES if previous was incomplete */
532 ✗ if (ty->pes_buf_cnt > 0) {
533 ✗ need = ty->pes_length - ty->pes_buf_cnt;
534
535 ff_dlog(s, "continuing PES header\n");
536 /* do we have enough data to complete? */
537 ✗ if (need >= rec_size) {
538 /* don't have complete PES hdr; save what we have and return */
539 ✗ memcpy(ty->pes_buffer + ty->pes_buf_cnt, ty->chunk + ty->cur_chunk_pos, rec_size);
540 ✗ ty->cur_chunk_pos += rec_size;
541 ✗ ty->pes_buf_cnt += rec_size;
542 ✗ return 0;
543 }
544
545 /* we have enough; reconstruct this frame with the new hdr */
546 ✗ memcpy(ty->pes_buffer + ty->pes_buf_cnt, ty->chunk + ty->cur_chunk_pos, need);
547 ✗ ty->cur_chunk_pos += need;
548 /* get the PTS out of this PES header (MPEG or AC3) */
549 ✗ if (ty->audio_type == TIVO_AUDIO_MPEG) {
550 ✗ es_offset1 = find_es_header(ty_MPEGAudioPacket,
551 ✗ ty->pes_buffer, 5);
552 } else {
553 ✗ es_offset1 = find_es_header(ty_AC3AudioPacket,
554 ✗ ty->pes_buffer, 5);
555 }
556 ✗ if (es_offset1 < 0) {
557 ff_dlog(s, "Can't find audio PES header in packet.\n");
558 } else {
559 ✗ ty->last_audio_pts = ff_parse_pes_pts(
560 ✗ &ty->pes_buffer[es_offset1 + ty->pts_offset]);
561 ✗ pkt->pts = ty->last_audio_pts;
562 }
563 ✗ ty->pes_buf_cnt = 0;
564
565 }
566 ✗ if ((ret = av_new_packet(pkt, rec_size - need)) < 0)
567 ✗ return ret;
568 ✗ memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size - need);
569 ✗ ty->cur_chunk_pos += rec_size - need;
570 ✗ pkt->stream_index = 1;
571
572 /* S2 DTivo has AC3 packets with 2 padding bytes at end. This is
573 * not allowed in the AC3 spec and will cause problems. So here
574 * we try to trim things. */
575 /* Also, S1 DTivo has alternating short / long AC3 packets. That
576 * is, one packet is short (incomplete) and the next packet has
577 * the first one's missing data, plus all of its own. Strange. */
578 ✗ if (ty->audio_type == TIVO_AUDIO_AC3 &&
579 ✗ ty->tivo_series == TIVO_SERIES2) {
580 ✗ if (ty->ac3_pkt_size + pkt->size > AC3_PKT_LENGTH) {
581 ✗ pkt->size -= FFMIN(pkt->size, 2);
582 ✗ ty->ac3_pkt_size = 0;
583 } else {
584 ✗ ty->ac3_pkt_size += pkt->size;
585 }
586 }
587 ✗ } else if (subrec_type == 0x03) {
588 ✗ if ((ret = av_new_packet(pkt, rec_size)) < 0)
589 ✗ return ret;
590 ✗ memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
591 ✗ ty->cur_chunk_pos += rec_size;
592 ✗ pkt->stream_index = 1;
593 /* MPEG Audio with PES Header, either SA or DTiVo */
594 /* ================================================ */
595 ✗ es_offset1 = find_es_header(ty_MPEGAudioPacket, pkt->data, 5);
596
597 /* SA PES Header, No Audio Data */
598 /* ================================================ */
599 ✗ if ((es_offset1 == 0) && (rec_size == 16)) {
600 ✗ ty->last_audio_pts = ff_parse_pes_pts(&pkt->data[SA_PTS_OFFSET]);
601 ✗ if (ty->first_audio_pts == AV_NOPTS_VALUE)
602 ✗ ty->first_audio_pts = ty->last_audio_pts;
603 ✗ av_packet_unref(pkt);
604 ✗ return 0;
605 }
606 /* DTiVo Audio with PES Header */
607 /* ================================================ */
608
609 /* Check for complete PES */
610 ✗ if (check_sync_pes(s, pkt, es_offset1, rec_size) == -1) {
611 /* partial PES header found, nothing else.
612 * we're done. */
613 ✗ av_packet_unref(pkt);
614 ✗ return 0;
615 }
616 ✗ } else if (subrec_type == 0x04) {
617 /* SA Audio with no PES Header */
618 /* ================================================ */
619 ✗ if ((ret = av_new_packet(pkt, rec_size)) < 0)
620 ✗ return ret;
621 ✗ memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
622 ✗ ty->cur_chunk_pos += rec_size;
623 ✗ pkt->stream_index = 1;
624 ✗ pkt->pts = ty->last_audio_pts;
625 ✗ } else if (subrec_type == 0x09) {
626 ✗ if ((ret = av_new_packet(pkt, rec_size)) < 0)
627 ✗ return ret;
628 ✗ memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
629 ✗ ty->cur_chunk_pos += rec_size ;
630 ✗ pkt->stream_index = 1;
631
632 /* DTiVo AC3 Audio Data with PES Header */
633 /* ================================================ */
634 ✗ es_offset1 = find_es_header(ty_AC3AudioPacket, pkt->data, 5);
635
636 /* Check for complete PES */
637 ✗ if (check_sync_pes(s, pkt, es_offset1, rec_size) == -1) {
638 /* partial PES header found, nothing else. we're done. */
639 ✗ av_packet_unref(pkt);
640 ✗ return 0;
641 }
642 /* S2 DTivo has invalid long AC3 packets */
643 ✗ if (ty->tivo_series == TIVO_SERIES2) {
644 ✗ if (pkt->size > AC3_PKT_LENGTH) {
645 ✗ pkt->size -= 2;
646 ✗ ty->ac3_pkt_size = 0;
647 } else {
648 ✗ ty->ac3_pkt_size = pkt->size;
649 }
650 }
651 } else {
652 /* Unsupported/Unknown */
653 ✗ ty->cur_chunk_pos += rec_size;
654 ✗ return 0;
655 }
656
657 ✗ return 1;
658 }
659
660 ✗ static int ty_read_packet(AVFormatContext *s, AVPacket *pkt)
661 {
662 ✗ TYDemuxContext *ty = s->priv_data;
663 ✗ AVIOContext *pb = s->pb;
664 TyRecHdr *rec;
665 ✗ int64_t rec_size = 0;
666 ✗ int ret = 0;
667
668 ✗ if (avio_feof(pb))
669 ✗ return AVERROR_EOF;
670
671 ✗ while (ret <= 0) {
672 ✗ if (!ty->rec_hdrs || ty->first_chunk || ty->cur_rec >= ty->num_recs) {
673 ✗ if (get_chunk(s) < 0 || ty->num_recs <= 0)
674 ✗ return AVERROR_EOF;
675 }
676
677 ✗ rec = &ty->rec_hdrs[ty->cur_rec];
678 ✗ rec_size = rec->rec_size;
679 ✗ ty->cur_rec++;
680
681 ✗ if (rec_size <= 0)
682 ✗ continue;
683
684 ✗ if (ty->cur_chunk_pos + rec->rec_size > CHUNK_SIZE)
685 ✗ return AVERROR_INVALIDDATA;
686
687 ✗ if (avio_feof(pb))
688 ✗ return AVERROR_EOF;
689
690 ✗ switch (rec->rec_type) {
691 ✗ case VIDEO_ID:
692 ✗ ret = demux_video(s, rec, pkt);
693 ✗ break;
694 ✗ case AUDIO_ID:
695 ✗ ret = demux_audio(s, rec, pkt);
696 ✗ break;
697 ✗ default:
698 ff_dlog(s, "Invalid record type 0x%02x\n", rec->rec_type);
699 av_fallthrough;
700 case 0x01:
701 case 0x02:
702 case 0x03: /* TiVo data services */
703 case 0x05: /* unknown, but seen regularly */
704 ✗ ty->cur_chunk_pos += rec->rec_size;
705 ✗ break;
706 }
707 }
708
709 ✗ return 0;
710 }
711
712 ✗ static int ty_read_close(AVFormatContext *s)
713 {
714 ✗ TYDemuxContext *ty = s->priv_data;
715
716 ✗ av_freep(&ty->rec_hdrs);
717
718 ✗ return 0;
719 }
720
721 const FFInputFormat ff_ty_demuxer = {
722 .p.name = "ty",
723 .p.long_name = NULL_IF_CONFIG_SMALL("TiVo TY Stream"),
724 .p.extensions = "ty,ty+",
725 .p.flags = AVFMT_TS_DISCONT,
726 .priv_data_size = sizeof(TYDemuxContext),
727 .read_probe = ty_probe,
728 .read_header = ty_read_header,
729 .read_packet = ty_read_packet,
730 .read_close = ty_read_close,
731 };
732