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
9 * This file is part of FFmpeg.
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.
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.
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
26 #include "libavutil/intreadwrite.h"
31 #define SERIES1_PES_LENGTH 11 /* length of audio PES hdr on S1 */
32 #define SERIES2_PES_LENGTH 16 /* length of audio PES hdr on S2 */
33 #define AC3_PES_LENGTH 14 /* length of audio PES hdr for AC3 */
34 #define VIDEO_PES_LENGTH 16 /* length of video PES header */
35 #define DTIVO_PTS_OFFSET 6 /* offs into PES for MPEG PTS on DTivo */
36 #define SA_PTS_OFFSET 9 /* offset into PES for MPEG PTS on SA */
37 #define AC3_PTS_OFFSET 9 /* offset into PES for AC3 PTS on DTivo */
38 #define VIDEO_PTS_OFFSET 9 /* offset into PES for video PTS on all */
39 #define AC3_PKT_LENGTH 1536 /* size of TiVo AC3 pkts (w/o PES hdr) */
41 static const uint8_t ty_VideoPacket[] = { 0x00, 0x00, 0x01, 0xe0 };
42 static const uint8_t ty_MPEGAudioPacket[] = { 0x00, 0x00, 0x01, 0xc0 };
43 static const uint8_t ty_AC3AudioPacket[] = { 0x00, 0x00, 0x01, 0xbd };
45 #define TIVO_PES_FILEID 0xf5467abd
46 #define CHUNK_SIZE (128 * 1024)
47 #define CHUNK_PEEK_COUNT 3 /* number of chunks to probe */
49 typedef struct TyRecHdr {
54 uint64_t ty_pts; /* TY PTS in the record header */
75 typedef struct TySeqTable {
77 uint8_t chunk_bitmask[8];
80 typedef struct TYDemuxContext {
82 unsigned cur_chunk_pos;
84 TiVo_type tivo_type; /* TiVo type (SA / DTiVo) */
85 TiVo_series tivo_series; /* Series1 or Series2 */
86 TiVo_audio audio_type; /* AC3 or MPEG */
87 int pes_length; /* Length of Audio PES header */
88 int pts_offset; /* offset into audio PES of PTS */
89 uint8_t pes_buffer[20]; /* holds incomplete pes headers */
90 int pes_buf_cnt; /* how many bytes in our buffer */
91 size_t ac3_pkt_size; /* length of ac3 pkt we've seen so far */
92 uint64_t last_ty_pts; /* last TY timestamp we've seen */
93 unsigned seq_table_size; /* number of entries in SEQ table */
95 int64_t first_audio_pts;
96 int64_t last_audio_pts;
97 int64_t last_video_pts;
99 TyRecHdr *rec_hdrs; /* record headers array */
100 int cur_rec; /* current record in this chunk */
101 int num_recs; /* number of recs in this chunk */
102 int seq_rec; /* record number where seq start is */
103 TySeqTable *seq_table; /* table of SEQ entries from mstr chk */
106 uint8_t chunk[CHUNK_SIZE];
109 static int ty_probe(AVProbeData *p)
113 for (i = 0; i + 12 < p->buf_size; i += CHUNK_SIZE) {
114 if (AV_RB32(p->buf + i) == TIVO_PES_FILEID &&
115 AV_RB32(p->buf + i + 4) == 0x02 &&
116 AV_RB32(p->buf + i + 8) == CHUNK_SIZE) {
117 return AVPROBE_SCORE_MAX;
124 static TyRecHdr *parse_chunk_headers(const uint8_t *buf,
127 TyRecHdr *hdrs, *rec_hdr;
130 hdrs = av_calloc(num_recs, sizeof(TyRecHdr));
134 for (i = 0; i < num_recs; i++) {
135 const uint8_t *record_header = buf + (i * 16);
137 rec_hdr = &hdrs[i]; /* for brevity */
138 rec_hdr->rec_type = record_header[3];
139 rec_hdr->subrec_type = record_header[2] & 0x0f;
140 if ((record_header[0] & 0x80) == 0x80) {
143 /* marker bit 2 set, so read extended data */
144 b1 = (((record_header[0] & 0x0f) << 4) |
145 ((record_header[1] & 0xf0) >> 4));
146 b2 = (((record_header[1] & 0x0f) << 4) |
147 ((record_header[2] & 0xf0) >> 4));
151 rec_hdr->rec_size = 0;
154 rec_hdr->rec_size = (record_header[0] << 8 |
155 record_header[1]) << 4 |
156 (record_header[2] >> 4);
157 rec_hdr->ty_pts = AV_RB64(&record_header[8]);
163 static int find_es_header(const uint8_t *header,
164 const uint8_t *buffer, int search_len)
168 for (count = 0; count < search_len; count++) {
169 if (!memcmp(&buffer[count], header, 4))
175 static int analyze_chunk(AVFormatContext *s, const uint8_t *chunk)
177 TYDemuxContext *ty = s->priv_data;
180 int num_6e0, num_be0, num_9c0, num_3c0;
182 /* skip if it's a Part header */
183 if (AV_RB32(&chunk[0]) == TIVO_PES_FILEID)
186 /* number of records in chunk (we ignore high order byte;
187 * rarely are there > 256 chunks & we don't need that many anyway) */
190 /* try again with the next chunk. Sometimes there are dead ones */
194 chunk += 4; /* skip past rec count & SEQ bytes */
195 ff_dlog(s, "probe: chunk has %d recs\n", num_recs);
196 hdrs = parse_chunk_headers(chunk, num_recs);
198 return AVERROR(ENOMEM);
201 * 1. check video packets. Presence of 0x6e0 means S1.
202 * No 6e0 but have be0 means S2.
203 * 2. probe for audio 0x9c0 vs 0x3c0 (AC3 vs Mpeg)
204 * If AC-3, then we have DTivo.
205 * If MPEG, search for PTS offset. This will determine SA vs. DTivo.
207 num_6e0 = num_be0 = num_9c0 = num_3c0 = 0;
208 for (i = 0; i < num_recs; i++) {
209 switch (hdrs[i].subrec_type << 8 | hdrs[i].rec_type) {
224 ff_dlog(s, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.\n",
227 /* set up our variables */
229 ff_dlog(s, "detected Series 1 Tivo\n");
230 ty->tivo_series = TIVO_SERIES1;
231 ty->pes_length = SERIES1_PES_LENGTH;
232 } else if (num_be0 > 0) {
233 ff_dlog(s, "detected Series 2 Tivo\n");
234 ty->tivo_series = TIVO_SERIES2;
235 ty->pes_length = SERIES2_PES_LENGTH;
238 ff_dlog(s, "detected AC-3 Audio (DTivo)\n");
239 ty->audio_type = TIVO_AUDIO_AC3;
240 ty->tivo_type = TIVO_TYPE_DTIVO;
241 ty->pts_offset = AC3_PTS_OFFSET;
242 ty->pes_length = AC3_PES_LENGTH;
243 } else if (num_3c0 > 0) {
244 ty->audio_type = TIVO_AUDIO_MPEG;
245 ff_dlog(s, "detected MPEG Audio\n");
248 /* if tivo_type still unknown, we can check PTS location
249 * in MPEG packets to determine tivo_type */
250 if (ty->tivo_type == TIVO_TYPE_UNKNOWN) {
251 uint32_t data_offset = 16 * num_recs;
253 for (i = 0; i < num_recs; i++) {
254 if (data_offset + hdrs[i].rec_size > CHUNK_SIZE)
257 if ((hdrs[i].subrec_type << 8 | hdrs[i].rec_type) == 0x3c0 && hdrs[i].rec_size > 15) {
258 /* first make sure we're aligned */
259 int pes_offset = find_es_header(ty_MPEGAudioPacket,
260 &chunk[data_offset], 5);
261 if (pes_offset >= 0) {
262 /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
263 if ((chunk[data_offset + 6 + pes_offset] & 0x80) == 0x80) {
264 /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
265 if (ty->tivo_series == TIVO_SERIES1)
266 ff_dlog(s, "detected Stand-Alone Tivo\n");
267 ty->tivo_type = TIVO_TYPE_SA;
268 ty->pts_offset = SA_PTS_OFFSET;
270 if (ty->tivo_series == TIVO_SERIES1)
271 ff_dlog(s, "detected DirecTV Tivo\n");
272 ty->tivo_type = TIVO_TYPE_DTIVO;
273 ty->pts_offset = DTIVO_PTS_OFFSET;
278 data_offset += hdrs[i].rec_size;
286 static int ty_read_header(AVFormatContext *s)
288 TYDemuxContext *ty = s->priv_data;
289 AVIOContext *pb = s->pb;
293 ty->first_audio_pts = AV_NOPTS_VALUE;
294 ty->last_audio_pts = AV_NOPTS_VALUE;
295 ty->last_video_pts = AV_NOPTS_VALUE;
297 for (i = 0; i < CHUNK_PEEK_COUNT; i++) {
298 avio_read(pb, ty->chunk, CHUNK_SIZE);
300 ret = analyze_chunk(s, ty->chunk);
303 if (ty->tivo_series != TIVO_SERIES_UNKNOWN &&
304 ty->audio_type != TIVO_AUDIO_UNKNOWN &&
305 ty->tivo_type != TIVO_TYPE_UNKNOWN)
309 if (ty->tivo_series == TIVO_SERIES_UNKNOWN ||
310 ty->audio_type == TIVO_AUDIO_UNKNOWN ||
311 ty->tivo_type == TIVO_TYPE_UNKNOWN)
314 st = avformat_new_stream(s, NULL);
316 return AVERROR(ENOMEM);
317 st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
318 st->codecpar->codec_id = AV_CODEC_ID_MPEG2VIDEO;
319 st->need_parsing = AVSTREAM_PARSE_FULL_RAW;
320 avpriv_set_pts_info(st, 64, 1, 90000);
322 ast = avformat_new_stream(s, NULL);
324 return AVERROR(ENOMEM);
325 ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
327 if (ty->audio_type == TIVO_AUDIO_MPEG) {
328 ast->codecpar->codec_id = AV_CODEC_ID_MP2;
329 ast->need_parsing = AVSTREAM_PARSE_FULL_RAW;
331 ast->codecpar->codec_id = AV_CODEC_ID_AC3;
333 avpriv_set_pts_info(ast, 64, 1, 90000);
337 avio_seek(pb, 0, SEEK_SET);
342 /* parse a master chunk, filling the SEQ table and other variables.
343 * We assume the stream is currently pointing to it.
345 static void parse_master(AVFormatContext *s)
347 TYDemuxContext *ty = s->priv_data;
348 unsigned map_size; /* size of bitmask, in bytes */
351 /* Note that the entries in the SEQ table in the stream may have
352 different sizes depending on the bits per entry. We store them
353 all in the same size structure, so we have to parse them out one
354 by one. If we had a dynamic structure, we could simply read the
355 entire table directly from the stream into memory in place. */
357 /* clear the SEQ table */
358 av_freep(&ty->seq_table);
360 /* parse header info */
362 map_size = AV_RB32(ty->chunk + 20); /* size of bitmask, in bytes */
363 i = AV_RB32(ty->chunk + 28); /* size of SEQ table, in bytes */
365 ty->seq_table_size = i / (8LL + map_size);
367 if (ty->seq_table_size == 0) {
368 ty->seq_table = NULL;
372 /* parse all the entries */
373 ty->seq_table = av_calloc(ty->seq_table_size, sizeof(TySeqTable));
374 if (ty->seq_table == NULL) {
375 ty->seq_table_size = 0;
379 ty->cur_chunk_pos = 32;
380 for (j = 0; j < ty->seq_table_size; j++) {
381 if (ty->cur_chunk_pos >= CHUNK_SIZE - 8)
383 ty->seq_table[j].timestamp = AV_RB64(ty->chunk + ty->cur_chunk_pos);
384 ty->cur_chunk_pos += 8;
386 av_log(s, AV_LOG_ERROR, "Unsupported SEQ bitmap size in master chunk.\n");
387 ty->cur_chunk_pos += map_size;
389 memcpy(ty->seq_table[j].chunk_bitmask, ty->chunk + ty->cur_chunk_pos, map_size);
394 static int get_chunk(AVFormatContext *s)
396 TYDemuxContext *ty = s->priv_data;
397 AVIOContext *pb = s->pb;
398 int read_size, num_recs;
400 ff_dlog(s, "parsing ty chunk #%d\n", ty->cur_chunk);
402 /* if we have left-over filler space from the last chunk, get that */
406 /* read the TY packet header */
407 read_size = avio_read(pb, ty->chunk, CHUNK_SIZE);
410 if ((read_size < 4) || (AV_RB32(ty->chunk) == 0)) {
414 /* check if it's a PART Header */
415 if (AV_RB32(ty->chunk) == TIVO_PES_FILEID) {
416 parse_master(s); /* parse master chunk */
420 /* number of records in chunk (8- or 16-bit number) */
421 if (ty->chunk[3] & 0x80) {
423 ty->num_recs = num_recs = (ty->chunk[1] << 8) + ty->chunk[0];
424 ty->seq_rec = (ty->chunk[3] << 8) + ty->chunk[2];
425 if (ty->seq_rec != 0xffff) {
426 ty->seq_rec &= ~0x8000;
429 /* 8 bit reclen - TiVo 1.3 format */
430 ty->num_recs = num_recs = ty->chunk[0];
431 ty->seq_rec = ty->chunk[1];
436 ff_dlog(s, "chunk has %d records\n", num_recs);
437 ty->cur_chunk_pos = 4;
439 av_freep(&ty->rec_hdrs);
441 if (num_recs * 16 >= CHUNK_SIZE - 4)
442 return AVERROR_INVALIDDATA;
444 ty->rec_hdrs = parse_chunk_headers(ty->chunk + 4, num_recs);
446 return AVERROR(ENOMEM);
447 ty->cur_chunk_pos += 16 * num_recs;
452 static int demux_video(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt)
454 TYDemuxContext *ty = s->priv_data;
455 const int subrec_type = rec_hdr->subrec_type;
456 const int64_t rec_size = rec_hdr->rec_size;
460 if (subrec_type != 0x02 && subrec_type != 0x0c &&
461 subrec_type != 0x08 && rec_size > 4) {
462 /* get the PTS from this packet if it has one.
463 * on S1, only 0x06 has PES. On S2, however, most all do.
464 * Do NOT Pass the PES Header to the MPEG2 codec */
465 es_offset1 = find_es_header(ty_VideoPacket, ty->chunk + ty->cur_chunk_pos, 5);
466 if (es_offset1 != -1) {
467 ty->last_video_pts = ff_parse_pes_pts(
468 ty->chunk + ty->cur_chunk_pos + es_offset1 + VIDEO_PTS_OFFSET);
469 if (subrec_type != 0x06) {
470 /* if we found a PES, and it's not type 6, then we're S2 */
471 /* The packet will have video data (& other headers) so we
472 * chop out the PES header and send the rest */
473 if (rec_size >= VIDEO_PES_LENGTH + es_offset1) {
474 int size = rec_hdr->rec_size - VIDEO_PES_LENGTH - es_offset1;
476 ty->cur_chunk_pos += VIDEO_PES_LENGTH + es_offset1;
477 if (av_new_packet(pkt, size) < 0)
478 return AVERROR(ENOMEM);
479 memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, size);
480 ty->cur_chunk_pos += size;
481 pkt->stream_index = 0;
484 ff_dlog(s, "video rec type 0x%02x has short PES"
485 " (%"PRId64" bytes)\n", subrec_type, rec_size);
486 /* nuke this block; it's too short, but has PES marker */
487 ty->cur_chunk_pos += rec_size;
494 if (subrec_type == 0x06) {
495 /* type 6 (S1 DTivo) has no data, so we're done */
496 ty->cur_chunk_pos += rec_size;
501 if (av_new_packet(pkt, rec_size) < 0)
502 return AVERROR(ENOMEM);
503 memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
504 ty->cur_chunk_pos += rec_size;
505 pkt->stream_index = 0;
509 /* if it's not a continue blk, then set PTS */
510 if (subrec_type != 0x02) {
511 if (subrec_type == 0x0c && pkt->size >= 6)
512 pkt->data[5] |= 0x08;
513 if (subrec_type == 0x07) {
514 ty->last_ty_pts = rec_hdr->ty_pts;
516 /* yes I know this is a cheap hack. It's the timestamp
517 used for display and skipping fwd/back, so it
518 doesn't have to be accurate to the millisecond.
519 I adjust it here by roughly one 1/30 sec. Yes it
520 will be slightly off for UK streams, but it's OK.
522 ty->last_ty_pts += 35000000;
523 //ty->last_ty_pts += 33366667;
525 /* set PTS for this block before we send */
526 if (ty->last_video_pts > AV_NOPTS_VALUE) {
527 pkt->pts = ty->last_video_pts;
528 /* PTS gets used ONCE.
529 * Any subsequent frames we get BEFORE next PES
530 * header will have their PTS computed in the codec */
531 ty->last_video_pts = AV_NOPTS_VALUE;
538 static int check_sync_pes(AVFormatContext *s, AVPacket *pkt,
539 int32_t offset, int32_t rec_len)
541 TYDemuxContext *ty = s->priv_data;
543 if (offset < 0 || offset + ty->pes_length > rec_len) {
544 /* entire PES header not present */
545 ff_dlog(s, "PES header at %"PRId32" not complete in record. storing.\n", offset);
546 /* save the partial pes header */
548 /* no header found, fake some 00's (this works, believe me) */
549 memset(ty->pes_buffer, 0, 4);
552 ff_dlog(s, "PES header not found in record of %"PRId32" bytes!\n", rec_len);
555 /* copy the partial pes header we found */
556 memcpy(ty->pes_buffer, pkt->data + offset, rec_len - offset);
557 ty->pes_buf_cnt = rec_len - offset;
560 /* PES Header was found, but not complete, so trim the end of this record */
561 pkt->size -= rec_len - offset;
564 return -1; /* partial PES, no audio data */
566 /* full PES header present, extract PTS */
567 ty->last_audio_pts = ff_parse_pes_pts(&pkt->data[ offset + ty->pts_offset]);
568 if (ty->first_audio_pts == AV_NOPTS_VALUE)
569 ty->first_audio_pts = ty->last_audio_pts;
570 pkt->pts = ty->last_audio_pts;
571 memmove(pkt->data + offset, pkt->data + offset + ty->pes_length, rec_len - ty->pes_length);
572 pkt->size -= ty->pes_length;
576 static int demux_audio(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt)
578 TYDemuxContext *ty = s->priv_data;
579 const int subrec_type = rec_hdr->subrec_type;
580 const int64_t rec_size = rec_hdr->rec_size;
583 if (subrec_type == 2) {
585 /* SA or DTiVo Audio Data, no PES (continued block)
586 * ================================================
589 /* continue PES if previous was incomplete */
590 if (ty->pes_buf_cnt > 0) {
591 need = ty->pes_length - ty->pes_buf_cnt;
593 ff_dlog(s, "continuing PES header\n");
594 /* do we have enough data to complete? */
595 if (need >= rec_size) {
596 /* don't have complete PES hdr; save what we have and return */
597 memcpy(ty->pes_buffer + ty->pes_buf_cnt, ty->chunk + ty->cur_chunk_pos, rec_size);
598 ty->cur_chunk_pos += rec_size;
599 ty->pes_buf_cnt += rec_size;
603 /* we have enough; reconstruct this frame with the new hdr */
604 memcpy(ty->pes_buffer + ty->pes_buf_cnt, ty->chunk + ty->cur_chunk_pos, need);
605 ty->cur_chunk_pos += need;
606 /* get the PTS out of this PES header (MPEG or AC3) */
607 if (ty->audio_type == TIVO_AUDIO_MPEG) {
608 es_offset1 = find_es_header(ty_MPEGAudioPacket,
611 es_offset1 = find_es_header(ty_AC3AudioPacket,
614 if (es_offset1 < 0) {
615 ff_dlog(s, "Can't find audio PES header in packet.\n");
617 ty->last_audio_pts = ff_parse_pes_pts(
618 &ty->pes_buffer[es_offset1 + ty->pts_offset]);
619 pkt->pts = ty->last_audio_pts;
624 if (av_new_packet(pkt, rec_size - need) < 0)
625 return AVERROR(ENOMEM);
626 memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size - need);
627 ty->cur_chunk_pos += rec_size - need;
628 pkt->stream_index = 1;
630 /* S2 DTivo has AC3 packets with 2 padding bytes at end. This is
631 * not allowed in the AC3 spec and will cause problems. So here
632 * we try to trim things. */
633 /* Also, S1 DTivo has alternating short / long AC3 packets. That
634 * is, one packet is short (incomplete) and the next packet has
635 * the first one's missing data, plus all of its own. Strange. */
636 if (ty->audio_type == TIVO_AUDIO_AC3 &&
637 ty->tivo_series == TIVO_SERIES2) {
638 if (ty->ac3_pkt_size + pkt->size > AC3_PKT_LENGTH) {
640 ty->ac3_pkt_size = 0;
642 ty->ac3_pkt_size += pkt->size;
645 } else if (subrec_type == 0x03) {
646 if (av_new_packet(pkt, rec_size) < 0)
647 return AVERROR(ENOMEM);
648 memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
649 ty->cur_chunk_pos += rec_size;
650 pkt->stream_index = 1;
651 /* MPEG Audio with PES Header, either SA or DTiVo */
652 /* ================================================ */
653 es_offset1 = find_es_header(ty_MPEGAudioPacket, pkt->data, 5);
655 /* SA PES Header, No Audio Data */
656 /* ================================================ */
657 if ((es_offset1 == 0) && (rec_size == 16)) {
658 ty->last_audio_pts = ff_parse_pes_pts(&pkt->data[SA_PTS_OFFSET]);
659 if (ty->first_audio_pts == AV_NOPTS_VALUE)
660 ty->first_audio_pts = ty->last_audio_pts;
661 av_packet_unref(pkt);
664 /* DTiVo Audio with PES Header */
665 /* ================================================ */
667 /* Check for complete PES */
668 if (check_sync_pes(s, pkt, es_offset1, rec_size) == -1) {
669 /* partial PES header found, nothing else.
671 av_packet_unref(pkt);
674 } else if (subrec_type == 0x04) {
675 /* SA Audio with no PES Header */
676 /* ================================================ */
677 if (av_new_packet(pkt, rec_size) < 0)
678 return AVERROR(ENOMEM);
679 memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
680 ty->cur_chunk_pos += rec_size;
681 pkt->stream_index = 1;
682 pkt->pts = ty->last_audio_pts;
683 } else if (subrec_type == 0x09) {
684 if (av_new_packet(pkt, rec_size) < 0)
685 return AVERROR(ENOMEM);
686 memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
687 ty->cur_chunk_pos += rec_size ;
688 pkt->stream_index = 1;
690 /* DTiVo AC3 Audio Data with PES Header */
691 /* ================================================ */
692 es_offset1 = find_es_header(ty_AC3AudioPacket, pkt->data, 5);
694 /* Check for complete PES */
695 if (check_sync_pes(s, pkt, es_offset1, rec_size) == -1) {
696 /* partial PES header found, nothing else. we're done. */
697 av_packet_unref(pkt);
700 /* S2 DTivo has invalid long AC3 packets */
701 if (ty->tivo_series == TIVO_SERIES2) {
702 if (pkt->size > AC3_PKT_LENGTH) {
704 ty->ac3_pkt_size = 0;
706 ty->ac3_pkt_size = pkt->size;
710 /* Unsupported/Unknown */
711 ty->cur_chunk_pos += rec_size;
718 static int ty_read_packet(AVFormatContext *s, AVPacket *pkt)
720 TYDemuxContext *ty = s->priv_data;
721 AVIOContext *pb = s->pb;
723 int64_t rec_size = 0;
730 if (!ty->rec_hdrs || ty->first_chunk || ty->cur_rec >= ty->num_recs) {
731 if (get_chunk(s) < 0 || ty->num_recs <= 0)
735 rec = &ty->rec_hdrs[ty->cur_rec];
736 rec_size = rec->rec_size;
742 if (ty->cur_chunk_pos + rec->rec_size > CHUNK_SIZE)
743 return AVERROR_INVALIDDATA;
748 switch (rec->rec_type) {
750 ret = demux_video(s, rec, pkt);
753 ret = demux_audio(s, rec, pkt);
756 ff_dlog(s, "Invalid record type 0x%02x\n", rec->rec_type);
759 case 0x03: /* TiVo data services */
760 case 0x05: /* unknown, but seen regularly */
761 ty->cur_chunk_pos += rec->rec_size;
769 static int ty_read_close(AVFormatContext *s)
771 TYDemuxContext *ty = s->priv_data;
773 av_freep(&ty->seq_table);
774 av_freep(&ty->rec_hdrs);
779 AVInputFormat ff_ty_demuxer = {
781 .long_name = NULL_IF_CONFIG_SMALL("TiVo TY Stream"),
782 .priv_data_size = sizeof(TYDemuxContext),
783 .read_probe = ty_probe,
784 .read_header = ty_read_header,
785 .read_packet = ty_read_packet,
786 .read_close = ty_read_close,
787 .extensions = "ty,ty+",
788 .flags = AVFMT_TS_DISCONT,