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