FFmpeg coverage


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