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 << 0x08 | 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 ty->seq_table[j].timestamp = AV_RB64(ty->chunk + ty->cur_chunk_pos);
382 ty->cur_chunk_pos += 8;
384 av_log(s, AV_LOG_ERROR, "Unsupported SEQ bitmap size in master chunk.\n");
385 ty->cur_chunk_pos += map_size;
387 memcpy(ty->seq_table[j].chunk_bitmask, ty->chunk + ty->cur_chunk_pos, map_size);
392 static int get_chunk(AVFormatContext *s)
394 TYDemuxContext *ty = s->priv_data;
395 AVIOContext *pb = s->pb;
396 int read_size, num_recs;
398 ff_dlog(s, "parsing ty chunk #%d\n", ty->cur_chunk);
400 /* if we have left-over filler space from the last chunk, get that */
404 /* read the TY packet header */
405 read_size = avio_read(pb, ty->chunk, CHUNK_SIZE);
408 if ((read_size < 4) || (AV_RB32(ty->chunk) == 0)) {
412 /* check if it's a PART Header */
413 if (AV_RB32(ty->chunk) == TIVO_PES_FILEID) {
414 parse_master(s); /* parse master chunk */
418 /* number of records in chunk (8- or 16-bit number) */
419 if (ty->chunk[3] & 0x80) {
421 ty->num_recs = num_recs = (ty->chunk[1] << 8) + ty->chunk[0];
422 ty->seq_rec = (ty->chunk[3] << 8) + ty->chunk[2];
423 if (ty->seq_rec != 0xffff) {
424 ty->seq_rec &= ~0x8000;
427 /* 8 bit reclen - TiVo 1.3 format */
428 ty->num_recs = num_recs = ty->chunk[0];
429 ty->seq_rec = ty->chunk[1];
434 ff_dlog(s, "chunk has %d records\n", num_recs);
435 ty->cur_chunk_pos = 4;
437 av_freep(&ty->rec_hdrs);
439 if (num_recs * 16 >= CHUNK_SIZE - 4)
440 return AVERROR_INVALIDDATA;
442 ty->rec_hdrs = parse_chunk_headers(ty->chunk + 4, num_recs);
443 ty->cur_chunk_pos += 16 * num_recs;
448 static int demux_video(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt)
450 TYDemuxContext *ty = s->priv_data;
451 const int subrec_type = rec_hdr->subrec_type;
452 const int64_t rec_size = rec_hdr->rec_size;
456 if (subrec_type != 0x02 && subrec_type != 0x0c &&
457 subrec_type != 0x08 && rec_size > 4) {
458 /* get the PTS from this packet if it has one.
459 * on S1, only 0x06 has PES. On S2, however, most all do.
460 * Do NOT Pass the PES Header to the MPEG2 codec */
461 es_offset1 = find_es_header(ty_VideoPacket, ty->chunk + ty->cur_chunk_pos, 5);
462 if (es_offset1 != -1) {
463 ty->last_video_pts = ff_parse_pes_pts(
464 ty->chunk + ty->cur_chunk_pos + es_offset1 + VIDEO_PTS_OFFSET);
465 if (subrec_type != 0x06) {
466 /* if we found a PES, and it's not type 6, then we're S2 */
467 /* The packet will have video data (& other headers) so we
468 * chop out the PES header and send the rest */
469 if (rec_size >= VIDEO_PES_LENGTH + es_offset1) {
470 int size = rec_hdr->rec_size - VIDEO_PES_LENGTH - es_offset1;
472 ty->cur_chunk_pos += VIDEO_PES_LENGTH + es_offset1;
473 if (av_new_packet(pkt, size) < 0)
474 return AVERROR(ENOMEM);
475 memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, size);
476 ty->cur_chunk_pos += size;
477 pkt->stream_index = 0;
480 ff_dlog(s, "video rec type 0x%02x has short PES"
481 " (%"PRId64" bytes)\n", subrec_type, rec_size);
482 /* nuke this block; it's too short, but has PES marker */
483 ty->cur_chunk_pos += rec_size;
490 if (subrec_type == 0x06) {
491 /* type 6 (S1 DTivo) has no data, so we're done */
492 ty->cur_chunk_pos += rec_size;
497 if (av_new_packet(pkt, rec_size) < 0)
498 return AVERROR(ENOMEM);
499 memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
500 ty->cur_chunk_pos += rec_size;
501 pkt->stream_index = 0;
505 /* if it's not a continue blk, then set PTS */
506 if (subrec_type != 0x02) {
507 if (subrec_type == 0x0c && pkt->size >= 6)
508 pkt->data[5] |= 0x08;
509 if (subrec_type == 0x07) {
510 ty->last_ty_pts = rec_hdr->ty_pts;
512 /* yes I know this is a cheap hack. It's the timestamp
513 used for display and skipping fwd/back, so it
514 doesn't have to be accurate to the millisecond.
515 I adjust it here by roughly one 1/30 sec. Yes it
516 will be slightly off for UK streams, but it's OK.
518 ty->last_ty_pts += 35000000;
519 //ty->last_ty_pts += 33366667;
521 /* set PTS for this block before we send */
522 if (ty->last_video_pts > AV_NOPTS_VALUE) {
523 pkt->pts = ty->last_video_pts;
524 /* PTS gets used ONCE.
525 * Any subsequent frames we get BEFORE next PES
526 * header will have their PTS computed in the codec */
527 ty->last_video_pts = AV_NOPTS_VALUE;
534 static int check_sync_pes(AVFormatContext *s, AVPacket *pkt,
535 int32_t offset, int32_t rec_len)
537 TYDemuxContext *ty = s->priv_data;
539 if (offset < 0 || offset + ty->pes_length > rec_len) {
540 /* entire PES header not present */
541 ff_dlog(s, "PES header at %"PRId32" not complete in record. storing.\n", offset);
542 /* save the partial pes header */
544 /* no header found, fake some 00's (this works, believe me) */
545 memset(ty->pes_buffer, 0, 4);
548 ff_dlog(s, "PES header not found in record of %"PRId32" bytes!\n", rec_len);
551 /* copy the partial pes header we found */
552 memcpy(ty->pes_buffer, pkt->data + offset, rec_len - offset);
553 ty->pes_buf_cnt = rec_len - offset;
556 /* PES Header was found, but not complete, so trim the end of this record */
557 pkt->size -= rec_len - offset;
560 return -1; /* partial PES, no audio data */
562 /* full PES header present, extract PTS */
563 ty->last_audio_pts = ff_parse_pes_pts(&pkt->data[ offset + ty->pts_offset]);
564 if (ty->first_audio_pts == AV_NOPTS_VALUE)
565 ty->first_audio_pts = ty->last_audio_pts;
566 pkt->pts = ty->last_audio_pts;
567 memmove(pkt->data + offset, pkt->data + offset + ty->pes_length, rec_len - ty->pes_length);
568 pkt->size -= ty->pes_length;
572 static int demux_audio(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt)
574 TYDemuxContext *ty = s->priv_data;
575 const int subrec_type = rec_hdr->subrec_type;
576 const int64_t rec_size = rec_hdr->rec_size;
579 if (subrec_type == 2) {
581 /* SA or DTiVo Audio Data, no PES (continued block)
582 * ================================================
585 /* continue PES if previous was incomplete */
586 if (ty->pes_buf_cnt > 0) {
587 need = ty->pes_length - ty->pes_buf_cnt;
589 ff_dlog(s, "continuing PES header\n");
590 /* do we have enough data to complete? */
591 if (need >= rec_size) {
592 /* don't have complete PES hdr; save what we have and return */
593 memcpy(ty->pes_buffer + ty->pes_buf_cnt, ty->chunk + ty->cur_chunk_pos, rec_size);
594 ty->cur_chunk_pos += rec_size;
595 ty->pes_buf_cnt += rec_size;
599 /* we have enough; reconstruct this frame with the new hdr */
600 memcpy(ty->pes_buffer + ty->pes_buf_cnt, ty->chunk + ty->cur_chunk_pos, need);
601 ty->cur_chunk_pos += need;
602 /* get the PTS out of this PES header (MPEG or AC3) */
603 if (ty->audio_type == TIVO_AUDIO_MPEG) {
604 es_offset1 = find_es_header(ty_MPEGAudioPacket,
607 es_offset1 = find_es_header(ty_AC3AudioPacket,
610 if (es_offset1 < 0) {
611 ff_dlog(s, "Can't find audio PES header in packet.\n");
613 ty->last_audio_pts = ff_parse_pes_pts(
614 &ty->pes_buffer[es_offset1 + ty->pts_offset]);
615 pkt->pts = ty->last_audio_pts;
620 if (av_new_packet(pkt, rec_size - need) < 0)
621 return AVERROR(ENOMEM);
622 memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size - need);
623 ty->cur_chunk_pos += rec_size - need;
624 pkt->stream_index = 1;
626 /* S2 DTivo has AC3 packets with 2 padding bytes at end. This is
627 * not allowed in the AC3 spec and will cause problems. So here
628 * we try to trim things. */
629 /* Also, S1 DTivo has alternating short / long AC3 packets. That
630 * is, one packet is short (incomplete) and the next packet has
631 * the first one's missing data, plus all of its own. Strange. */
632 if (ty->audio_type == TIVO_AUDIO_AC3 &&
633 ty->tivo_series == TIVO_SERIES2) {
634 if (ty->ac3_pkt_size + pkt->size > AC3_PKT_LENGTH) {
636 ty->ac3_pkt_size = 0;
638 ty->ac3_pkt_size += pkt->size;
641 } else if (subrec_type == 0x03) {
642 if (av_new_packet(pkt, rec_size) < 0)
643 return AVERROR(ENOMEM);
644 memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
645 ty->cur_chunk_pos += rec_size;
646 pkt->stream_index = 1;
647 /* MPEG Audio with PES Header, either SA or DTiVo */
648 /* ================================================ */
649 es_offset1 = find_es_header(ty_MPEGAudioPacket, pkt->data, 5);
651 /* SA PES Header, No Audio Data */
652 /* ================================================ */
653 if ((es_offset1 == 0) && (rec_size == 16)) {
654 ty->last_audio_pts = ff_parse_pes_pts(&pkt->data[SA_PTS_OFFSET]);
655 if (ty->first_audio_pts == AV_NOPTS_VALUE)
656 ty->first_audio_pts = ty->last_audio_pts;
657 av_packet_unref(pkt);
660 /* DTiVo Audio with PES Header */
661 /* ================================================ */
663 /* Check for complete PES */
664 if (check_sync_pes(s, pkt, es_offset1, rec_size) == -1) {
665 /* partial PES header found, nothing else.
667 av_packet_unref(pkt);
670 } else if (subrec_type == 0x04) {
671 /* SA Audio with no PES Header */
672 /* ================================================ */
673 if (av_new_packet(pkt, rec_size) < 0)
674 return AVERROR(ENOMEM);
675 memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
676 ty->cur_chunk_pos += rec_size;
677 pkt->stream_index = 1;
678 pkt->pts = ty->last_audio_pts;
679 } else if (subrec_type == 0x09) {
680 if (av_new_packet(pkt, rec_size) < 0)
681 return AVERROR(ENOMEM);
682 memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
683 ty->cur_chunk_pos += rec_size ;
684 pkt->stream_index = 1;
686 /* DTiVo AC3 Audio Data with PES Header */
687 /* ================================================ */
688 es_offset1 = find_es_header(ty_AC3AudioPacket, pkt->data, 5);
690 /* Check for complete PES */
691 if (check_sync_pes(s, pkt, es_offset1, rec_size) == -1) {
692 /* partial PES header found, nothing else. we're done. */
693 av_packet_unref(pkt);
696 /* S2 DTivo has invalid long AC3 packets */
697 if (ty->tivo_series == TIVO_SERIES2) {
698 if (pkt->size > AC3_PKT_LENGTH) {
700 ty->ac3_pkt_size = 0;
702 ty->ac3_pkt_size = pkt->size;
706 /* Unsupported/Unknown */
707 ty->cur_chunk_pos += rec_size;
714 static int ty_read_packet(AVFormatContext *s, AVPacket *pkt)
716 TYDemuxContext *ty = s->priv_data;
717 AVIOContext *pb = s->pb;
719 int64_t rec_size = 0;
726 if (ty->first_chunk || ty->cur_rec >= ty->num_recs) {
727 if (get_chunk(s) < 0 || ty->num_recs == 0)
731 rec = &ty->rec_hdrs[ty->cur_rec];
732 rec_size = rec->rec_size;
738 if (ty->cur_chunk_pos + rec->rec_size > CHUNK_SIZE)
739 return AVERROR_INVALIDDATA;
744 switch (rec->rec_type) {
746 ret = demux_video(s, rec, pkt);
749 ret = demux_audio(s, rec, pkt);
752 ff_dlog(s, "Invalid record type 0x%02x\n", rec->rec_type);
755 case 0x03: /* TiVo data services */
756 case 0x05: /* unknown, but seen regularly */
757 ty->cur_chunk_pos += rec->rec_size;
765 AVInputFormat ff_ty_demuxer = {
767 .long_name = NULL_IF_CONFIG_SMALL("TiVo TY Stream"),
768 .priv_data_size = sizeof(TYDemuxContext),
769 .read_probe = ty_probe,
770 .read_header = ty_read_header,
771 .read_packet = ty_read_packet,
772 .extensions = "ty,ty+",
773 .flags = AVFMT_TS_DISCONT,