Coverage Report

Created: 2026-01-16 07:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavformat/ty.c
Line
Count
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
306
#define SERIES1_PES_LENGTH  11    /* length of audio PES hdr on S1 */
34
986
#define SERIES2_PES_LENGTH  16    /* length of audio PES hdr on S2 */
35
312
#define AC3_PES_LENGTH      14    /* length of audio PES hdr for AC3 */
36
2.85k
#define VIDEO_PES_LENGTH    16    /* length of video PES header */
37
715
#define DTIVO_PTS_OFFSET    6     /* offs into PES for MPEG PTS on DTivo */
38
191
#define SA_PTS_OFFSET       9     /* offset into PES for MPEG PTS on SA */
39
312
#define AC3_PTS_OFFSET      9     /* offset into PES for AC3 PTS on DTivo */
40
964
#define VIDEO_PTS_OFFSET    9     /* offset into PES for video PTS on all */
41
107
#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
1.67M
#define TIVO_PES_FILEID   0xf5467abd
48
999k
#define CHUNK_SIZE        (128 * 1024)
49
3.20k
#define CHUNK_PEEK_COUNT  3      /* number of chunks to probe */
50
51
typedef struct TyRecHdr {
52
    int32_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
static int ty_probe(const AVProbeData *p)
104
958k
{
105
958k
    int i;
106
107
1.79M
    for (i = 0; i + 12 < p->buf_size; i += CHUNK_SIZE) {
108
837k
        if (AV_RB32(p->buf + i) == TIVO_PES_FILEID &&
109
580
            AV_RB32(p->buf + i + 4) == 0x02 &&
110
352
            AV_RB32(p->buf + i + 8) == CHUNK_SIZE) {
111
75
            return AVPROBE_SCORE_MAX;
112
75
        }
113
837k
    }
114
115
958k
    return 0;
116
958k
}
117
118
static TyRecHdr *parse_chunk_headers(const uint8_t *buf,
119
                                     int num_recs)
120
3.79k
{
121
3.79k
    TyRecHdr *hdrs, *rec_hdr;
122
3.79k
    int i;
123
124
3.79k
    hdrs = av_calloc(num_recs, sizeof(TyRecHdr));
125
3.79k
    if (!hdrs)
126
0
        return NULL;
127
128
980k
    for (i = 0; i < num_recs; i++) {
129
976k
        const uint8_t *record_header = buf + (i * 16);
130
131
976k
        rec_hdr = &hdrs[i];     /* for brevity */
132
976k
        rec_hdr->rec_type = record_header[3];
133
976k
        rec_hdr->subrec_type = record_header[2] & 0x0f;
134
976k
        if ((record_header[0] & 0x80) == 0x80) {
135
262k
            uint8_t b1, b2;
136
137
            /* marker bit 2 set, so read extended data */
138
262k
            b1 = (((record_header[0] & 0x0f) << 4) |
139
262k
                  ((record_header[1] & 0xf0) >> 4));
140
262k
            b2 = (((record_header[1] & 0x0f) << 4) |
141
262k
                  ((record_header[2] & 0xf0) >> 4));
142
143
262k
            rec_hdr->ex[0] = b1;
144
262k
            rec_hdr->ex[1] = b2;
145
262k
            rec_hdr->rec_size = 0;
146
262k
            rec_hdr->ty_pts = 0;
147
714k
        } else {
148
714k
            rec_hdr->rec_size = (record_header[0] << 8 |
149
714k
                                 record_header[1]) << 4 |
150
714k
                                (record_header[2] >> 4);
151
714k
            rec_hdr->ty_pts = AV_RB64(&record_header[8]);
152
714k
        }
153
976k
    }
154
3.79k
    return hdrs;
155
3.79k
}
156
157
static int find_es_header(const uint8_t *header,
158
                          const uint8_t *buffer, int search_len)
159
8.59k
{
160
8.59k
    int count;
161
162
45.1k
    for (count = 0; count < search_len; count++) {
163
39.3k
        if (!memcmp(&buffer[count], header, 4))
164
2.78k
            return count;
165
39.3k
    }
166
5.80k
    return -1;
167
8.59k
}
168
169
static int analyze_chunk(AVFormatContext *s, const uint8_t *chunk)
170
2.70k
{
171
2.70k
    TYDemuxContext *ty = s->priv_data;
172
2.70k
    int num_recs, i;
173
2.70k
    TyRecHdr *hdrs;
174
2.70k
    int num_6e0, num_be0, num_9c0, num_3c0;
175
176
    /* skip if it's a Part header */
177
2.70k
    if (AV_RB32(&chunk[0]) == TIVO_PES_FILEID)
178
13
        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
2.69k
    num_recs = chunk[0];
183
2.69k
    if (num_recs < 5) {
184
        /* try again with the next chunk.  Sometimes there are dead ones */
185
69
        return 0;
186
69
    }
187
188
2.62k
    chunk += 4;       /* skip past rec count & SEQ bytes */
189
2.62k
    ff_dlog(s, "probe: chunk has %d recs\n", num_recs);
190
2.62k
    hdrs = parse_chunk_headers(chunk, num_recs);
191
2.62k
    if (!hdrs)
192
0
        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
2.62k
    num_6e0 = num_be0 = num_9c0 = num_3c0 = 0;
202
431k
    for (i = 0; i < num_recs; i++) {
203
429k
        switch (hdrs[i].subrec_type << 8 | hdrs[i].rec_type) {
204
913
        case 0x6e0:
205
913
            num_6e0++;
206
913
            break;
207
6.19k
        case 0xbe0:
208
6.19k
            num_be0++;
209
6.19k
            break;
210
4.47k
        case 0x3c0:
211
4.47k
            num_3c0++;
212
4.47k
            break;
213
905
        case 0x9c0:
214
905
            num_9c0++;
215
905
            break;
216
429k
        }
217
429k
    }
218
2.62k
    ff_dlog(s, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.\n",
219
2.62k
            num_6e0, num_be0);
220
221
    /* set up our variables */
222
2.62k
    if (num_6e0 > 0) {
223
306
        ff_dlog(s, "detected Series 1 Tivo\n");
224
306
        ty->tivo_series = TIVO_SERIES1;
225
306
        ty->pes_length = SERIES1_PES_LENGTH;
226
2.31k
    } else if (num_be0 > 0) {
227
986
        ff_dlog(s, "detected Series 2 Tivo\n");
228
986
        ty->tivo_series = TIVO_SERIES2;
229
986
        ty->pes_length = SERIES2_PES_LENGTH;
230
986
    }
231
2.62k
    if (num_9c0 > 0) {
232
312
        ff_dlog(s, "detected AC-3 Audio (DTivo)\n");
233
312
        ty->audio_type = TIVO_AUDIO_AC3;
234
312
        ty->tivo_type = TIVO_TYPE_DTIVO;
235
312
        ty->pts_offset = AC3_PTS_OFFSET;
236
312
        ty->pes_length = AC3_PES_LENGTH;
237
2.31k
    } else if (num_3c0 > 0) {
238
1.17k
        ty->audio_type = TIVO_AUDIO_MPEG;
239
1.17k
        ff_dlog(s, "detected MPEG Audio\n");
240
1.17k
    }
241
242
    /* if tivo_type still unknown, we can check PTS location
243
     * in MPEG packets to determine tivo_type */
244
2.62k
    if (ty->tivo_type == TIVO_TYPE_UNKNOWN) {
245
2.28k
        uint32_t data_offset = 16 * num_recs;
246
247
141k
        for (i = 0; i < num_recs; i++) {
248
140k
            if (data_offset + hdrs[i].rec_size > CHUNK_SIZE)
249
375
                break;
250
251
140k
            if ((hdrs[i].subrec_type << 8 | hdrs[i].rec_type) == 0x3c0 && hdrs[i].rec_size > 15) {
252
                /* first make sure we're aligned */
253
1.64k
                int pes_offset = find_es_header(ty_MPEGAudioPacket,
254
1.64k
                        &chunk[data_offset], 5);
255
1.64k
                if (pes_offset >= 0) {
256
                    /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
257
903
                    if ((chunk[data_offset + 6 + pes_offset] & 0x80) == 0x80) {
258
                        /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
259
188
                        if (ty->tivo_series == TIVO_SERIES1)
260
44
                            ff_dlog(s, "detected Stand-Alone Tivo\n");
261
188
                        ty->tivo_type = TIVO_TYPE_SA;
262
188
                        ty->pts_offset = SA_PTS_OFFSET;
263
715
                    } else {
264
715
                        if (ty->tivo_series == TIVO_SERIES1)
265
28
                            ff_dlog(s, "detected DirecTV Tivo\n");
266
715
                        ty->tivo_type = TIVO_TYPE_DTIVO;
267
715
                        ty->pts_offset = DTIVO_PTS_OFFSET;
268
715
                    }
269
903
                    break;
270
903
                }
271
1.64k
            }
272
139k
            data_offset += hdrs[i].rec_size;
273
139k
        }
274
2.28k
    }
275
2.62k
    av_free(hdrs);
276
277
2.62k
    return 0;
278
2.62k
}
279
280
static int ty_read_header(AVFormatContext *s)
281
1.66k
{
282
1.66k
    TYDemuxContext *ty = s->priv_data;
283
1.66k
    AVIOContext *pb = s->pb;
284
1.66k
    AVStream *st, *ast;
285
1.66k
    int i, ret = 0;
286
287
1.66k
    ty->first_audio_pts = AV_NOPTS_VALUE;
288
1.66k
    ty->last_audio_pts = AV_NOPTS_VALUE;
289
1.66k
    ty->last_video_pts = AV_NOPTS_VALUE;
290
291
3.20k
    for (i = 0; i < CHUNK_PEEK_COUNT; i++) {
292
2.70k
        avio_read(pb, ty->chunk, CHUNK_SIZE);
293
294
2.70k
        ret = analyze_chunk(s, ty->chunk);
295
2.70k
        if (ret < 0)
296
0
            return ret;
297
2.70k
        if (ty->tivo_series != TIVO_SERIES_UNKNOWN &&
298
1.31k
            ty->audio_type  != TIVO_AUDIO_UNKNOWN &&
299
1.23k
            ty->tivo_type   != TIVO_TYPE_UNKNOWN)
300
1.16k
            break;
301
2.70k
    }
302
303
1.66k
    if (ty->tivo_series == TIVO_SERIES_UNKNOWN ||
304
1.20k
        ty->audio_type == TIVO_AUDIO_UNKNOWN ||
305
1.18k
        ty->tivo_type == TIVO_TYPE_UNKNOWN)
306
500
        return AVERROR_INVALIDDATA;
307
308
1.16k
    st = avformat_new_stream(s, NULL);
309
1.16k
    if (!st)
310
0
        return AVERROR(ENOMEM);
311
1.16k
    st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
312
1.16k
    st->codecpar->codec_id   = AV_CODEC_ID_MPEG2VIDEO;
313
1.16k
    ffstream(st)->need_parsing = AVSTREAM_PARSE_FULL_RAW;
314
1.16k
    avpriv_set_pts_info(st, 64, 1, 90000);
315
316
1.16k
    ast = avformat_new_stream(s, NULL);
317
1.16k
    if (!ast)
318
0
        return AVERROR(ENOMEM);
319
1.16k
    ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
320
321
1.16k
    if (ty->audio_type == TIVO_AUDIO_MPEG) {
322
895
        ast->codecpar->codec_id = AV_CODEC_ID_MP2;
323
895
        ffstream(ast)->need_parsing = AVSTREAM_PARSE_FULL_RAW;
324
895
    } else {
325
268
        ast->codecpar->codec_id = AV_CODEC_ID_AC3;
326
268
    }
327
1.16k
    avpriv_set_pts_info(ast, 64, 1, 90000);
328
329
1.16k
    ty->first_chunk = 1;
330
331
1.16k
    avio_seek(pb, 0, SEEK_SET);
332
333
1.16k
    return 0;
334
1.16k
}
335
336
static int get_chunk(AVFormatContext *s)
337
1.34k
{
338
1.34k
    TYDemuxContext *ty = s->priv_data;
339
1.34k
    AVIOContext *pb = s->pb;
340
1.34k
    int read_size, num_recs;
341
342
1.34k
    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
1.34k
    if (avio_feof(pb))
346
34
        return AVERROR_EOF;
347
348
    /* read the TY packet header */
349
1.31k
    read_size = avio_read(pb, ty->chunk, CHUNK_SIZE);
350
1.31k
    ty->cur_chunk++;
351
352
1.31k
    if ((read_size < 4) || (AV_RB32(ty->chunk) == 0)) {
353
82
        return AVERROR_EOF;
354
82
    }
355
356
    /* check if it's a PART Header */
357
1.23k
    if (AV_RB32(ty->chunk) == TIVO_PES_FILEID) {
358
        /* skip master chunk and read new chunk */
359
1
        return get_chunk(s);
360
1
    }
361
362
    /* number of records in chunk (8- or 16-bit number) */
363
1.22k
    if (ty->chunk[3] & 0x80) {
364
        /* 16 bit rec cnt */
365
169
        ty->num_recs = num_recs = (ty->chunk[1] << 8) + ty->chunk[0];
366
1.06k
    } else {
367
        /* 8 bit reclen - TiVo 1.3 format */
368
1.06k
        ty->num_recs = num_recs = ty->chunk[0];
369
1.06k
    }
370
1.22k
    ty->cur_rec = 0;
371
1.22k
    ty->first_chunk = 0;
372
373
1.22k
    ff_dlog(s, "chunk has %d records\n", num_recs);
374
1.22k
    ty->cur_chunk_pos = 4;
375
376
1.22k
    av_freep(&ty->rec_hdrs);
377
378
1.22k
    if (num_recs * 16 >= CHUNK_SIZE - 4)
379
60
        return AVERROR_INVALIDDATA;
380
381
1.16k
    ty->rec_hdrs = parse_chunk_headers(ty->chunk + 4, num_recs);
382
1.16k
    if (!ty->rec_hdrs)
383
0
        return AVERROR(ENOMEM);
384
1.16k
    ty->cur_chunk_pos += 16 * num_recs;
385
386
1.16k
    return 0;
387
1.16k
}
388
389
static int demux_video(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt)
390
5.28k
{
391
5.28k
    TYDemuxContext *ty = s->priv_data;
392
5.28k
    const int subrec_type = rec_hdr->subrec_type;
393
5.28k
    const int64_t rec_size = rec_hdr->rec_size;
394
5.28k
    int es_offset1, ret;
395
5.28k
    int got_packet = 0;
396
397
5.28k
    if (subrec_type != 0x02 && subrec_type != 0x0c &&
398
5.20k
        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
5.14k
        es_offset1 = find_es_header(ty_VideoPacket, ty->chunk + ty->cur_chunk_pos, 5);
403
5.14k
        if (es_offset1 != -1) {
404
964
            ty->last_video_pts = ff_parse_pes_pts(
405
964
                    ty->chunk + ty->cur_chunk_pos + es_offset1 + VIDEO_PTS_OFFSET);
406
964
            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
952
                if (rec_size >= VIDEO_PES_LENGTH + es_offset1) {
411
951
                    int size = rec_hdr->rec_size - VIDEO_PES_LENGTH - es_offset1;
412
413
951
                    ty->cur_chunk_pos += VIDEO_PES_LENGTH + es_offset1;
414
951
                    if ((ret = av_new_packet(pkt, size)) < 0)
415
0
                        return ret;
416
951
                    memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, size);
417
951
                    ty->cur_chunk_pos += size;
418
951
                    pkt->stream_index = 0;
419
951
                    got_packet = 1;
420
951
                } else {
421
1
                    ff_dlog(s, "video rec type 0x%02x has short PES"
422
1
                        " (%"PRId64" bytes)\n", subrec_type, rec_size);
423
                    /* nuke this block; it's too short, but has PES marker */
424
1
                    ty->cur_chunk_pos += rec_size;
425
1
                    return 0;
426
1
                }
427
952
            }
428
964
        }
429
5.14k
    }
430
431
5.27k
    if (subrec_type == 0x06) {
432
        /* type 6 (S1 DTivo) has no data, so we're done */
433
50
        ty->cur_chunk_pos += rec_size;
434
50
        return 0;
435
50
    }
436
437
5.22k
    if (!got_packet) {
438
4.27k
        if ((ret = av_new_packet(pkt, rec_size)) < 0)
439
0
            return ret;
440
4.27k
        memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
441
4.27k
        ty->cur_chunk_pos += rec_size;
442
4.27k
        pkt->stream_index = 0;
443
4.27k
        got_packet = 1;
444
4.27k
    }
445
446
    /* if it's not a continue blk, then set PTS */
447
5.22k
    if (subrec_type != 0x02) {
448
5.18k
        if (subrec_type == 0x0c && pkt->size >= 6)
449
16
            pkt->data[5] |= 0x08;
450
5.18k
        if (subrec_type == 0x07) {
451
20
            ty->last_ty_pts = rec_hdr->ty_pts;
452
5.16k
        } 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
5.16k
            ty->last_ty_pts += 35000000;
460
            //ty->last_ty_pts += 33366667;
461
5.16k
        }
462
        /* set PTS for this block before we send */
463
5.18k
        if (ty->last_video_pts > AV_NOPTS_VALUE) {
464
958
            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
958
            ty->last_video_pts = AV_NOPTS_VALUE;
469
958
        }
470
5.18k
    }
471
472
5.22k
    return got_packet;
473
5.22k
}
474
475
static int check_sync_pes(AVFormatContext *s, AVPacket *pkt,
476
                          int32_t offset, int32_t rec_len)
477
1.68k
{
478
1.68k
    TYDemuxContext *ty = s->priv_data;
479
480
1.68k
    if (offset < 0 || offset + ty->pes_length > rec_len) {
481
        /* entire PES header not present */
482
794
        ff_dlog(s, "PES header at %"PRId32" not complete in record. storing.\n", offset);
483
        /* save the partial pes header */
484
794
        if (offset < 0) {
485
            /* no header found, fake some 00's (this works, believe me) */
486
782
            memset(ty->pes_buffer, 0, 4);
487
782
            ty->pes_buf_cnt = 4;
488
782
            if (rec_len > 4)
489
668
                ff_dlog(s, "PES header not found in record of %"PRId32" bytes!\n", rec_len);
490
782
            return -1;
491
782
        }
492
        /* copy the partial pes header we found */
493
12
        memcpy(ty->pes_buffer, pkt->data + offset, rec_len - offset);
494
12
        ty->pes_buf_cnt = rec_len - offset;
495
496
12
        if (offset > 0) {
497
            /* PES Header was found, but not complete, so trim the end of this record */
498
10
            pkt->size -= rec_len - offset;
499
10
            return 1;
500
10
        }
501
2
        return -1;    /* partial PES, no audio data */
502
12
    }
503
    /* full PES header present, extract PTS */
504
887
    ty->last_audio_pts = ff_parse_pes_pts(&pkt->data[ offset + ty->pts_offset]);
505
887
    if (ty->first_audio_pts == AV_NOPTS_VALUE)
506
805
        ty->first_audio_pts = ty->last_audio_pts;
507
887
    pkt->pts = ty->last_audio_pts;
508
887
    memmove(pkt->data + offset, pkt->data + offset + ty->pes_length, rec_len - ty->pes_length);
509
887
    pkt->size -= ty->pes_length;
510
887
    return 0;
511
1.68k
}
512
513
static int demux_audio(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt)
514
4.19k
{
515
4.19k
    TYDemuxContext *ty = s->priv_data;
516
4.19k
    const int subrec_type = rec_hdr->subrec_type;
517
4.19k
    const int64_t rec_size = rec_hdr->rec_size;
518
4.19k
    int es_offset1, ret;
519
520
4.19k
    if (subrec_type == 2) {
521
552
        int need = 0;
522
        /* SA or DTiVo Audio Data, no PES (continued block)
523
         * ================================================
524
         */
525
526
        /* continue PES if previous was incomplete */
527
552
        if (ty->pes_buf_cnt > 0) {
528
132
            need = ty->pes_length - ty->pes_buf_cnt;
529
530
132
            ff_dlog(s, "continuing PES header\n");
531
            /* do we have enough data to complete? */
532
132
            if (need >= rec_size) {
533
                /* don't have complete PES hdr; save what we have and return */
534
7
                memcpy(ty->pes_buffer + ty->pes_buf_cnt, ty->chunk + ty->cur_chunk_pos, rec_size);
535
7
                ty->cur_chunk_pos += rec_size;
536
7
                ty->pes_buf_cnt += rec_size;
537
7
                return 0;
538
7
            }
539
540
            /* we have enough; reconstruct this frame with the new hdr */
541
125
            memcpy(ty->pes_buffer + ty->pes_buf_cnt, ty->chunk + ty->cur_chunk_pos, need);
542
125
            ty->cur_chunk_pos += need;
543
            /* get the PTS out of this PES header (MPEG or AC3) */
544
125
            if (ty->audio_type == TIVO_AUDIO_MPEG) {
545
86
                es_offset1 = find_es_header(ty_MPEGAudioPacket,
546
86
                        ty->pes_buffer, 5);
547
86
            } else {
548
39
                es_offset1 = find_es_header(ty_AC3AudioPacket,
549
39
                        ty->pes_buffer, 5);
550
39
            }
551
125
            if (es_offset1 < 0) {
552
109
                ff_dlog(s, "Can't find audio PES header in packet.\n");
553
109
            } else {
554
16
                ty->last_audio_pts = ff_parse_pes_pts(
555
16
                    &ty->pes_buffer[es_offset1 + ty->pts_offset]);
556
16
                pkt->pts = ty->last_audio_pts;
557
16
            }
558
125
            ty->pes_buf_cnt = 0;
559
560
125
        }
561
545
        if ((ret = av_new_packet(pkt, rec_size - need)) < 0)
562
0
            return ret;
563
545
        memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size - need);
564
545
        ty->cur_chunk_pos += rec_size - need;
565
545
        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
545
        if (ty->audio_type == TIVO_AUDIO_AC3 &&
574
141
                ty->tivo_series == TIVO_SERIES2) {
575
89
            if (ty->ac3_pkt_size + pkt->size > AC3_PKT_LENGTH) {
576
68
                pkt->size -= 2;
577
68
                ty->ac3_pkt_size = 0;
578
68
            } else {
579
21
                ty->ac3_pkt_size += pkt->size;
580
21
            }
581
89
        }
582
3.63k
    } else if (subrec_type == 0x03) {
583
1.61k
        if ((ret = av_new_packet(pkt, rec_size)) < 0)
584
0
            return ret;
585
1.61k
        memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
586
1.61k
        ty->cur_chunk_pos += rec_size;
587
1.61k
        pkt->stream_index = 1;
588
        /* MPEG Audio with PES Header, either SA or DTiVo   */
589
        /* ================================================ */
590
1.61k
        es_offset1 = find_es_header(ty_MPEGAudioPacket, pkt->data, 5);
591
592
        /* SA PES Header, No Audio Data                     */
593
        /* ================================================ */
594
1.61k
        if ((es_offset1 == 0) && (rec_size == 16)) {
595
3
            ty->last_audio_pts = ff_parse_pes_pts(&pkt->data[SA_PTS_OFFSET]);
596
3
            if (ty->first_audio_pts == AV_NOPTS_VALUE)
597
2
                ty->first_audio_pts = ty->last_audio_pts;
598
3
            av_packet_unref(pkt);
599
3
            return 0;
600
3
        }
601
        /* DTiVo Audio with PES Header                      */
602
        /* ================================================ */
603
604
        /* Check for complete PES */
605
1.61k
        if (check_sync_pes(s, pkt, es_offset1, rec_size) == -1) {
606
            /* partial PES header found, nothing else.
607
             * we're done. */
608
745
            av_packet_unref(pkt);
609
745
            return 0;
610
745
        }
611
2.02k
    } else if (subrec_type == 0x04) {
612
        /* SA Audio with no PES Header                      */
613
        /* ================================================ */
614
1.49k
        if ((ret = av_new_packet(pkt, rec_size)) < 0)
615
0
            return ret;
616
1.49k
        memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
617
1.49k
        ty->cur_chunk_pos += rec_size;
618
1.49k
        pkt->stream_index = 1;
619
1.49k
        pkt->pts = ty->last_audio_pts;
620
1.49k
    } else if (subrec_type == 0x09) {
621
70
        if ((ret = av_new_packet(pkt, rec_size)) < 0)
622
0
            return ret;
623
70
        memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
624
70
        ty->cur_chunk_pos += rec_size ;
625
70
        pkt->stream_index = 1;
626
627
        /* DTiVo AC3 Audio Data with PES Header             */
628
        /* ================================================ */
629
70
        es_offset1 = find_es_header(ty_AC3AudioPacket, pkt->data, 5);
630
631
        /* Check for complete PES */
632
70
        if (check_sync_pes(s, pkt, es_offset1, rec_size) == -1) {
633
            /* partial PES header found, nothing else.  we're done. */
634
39
            av_packet_unref(pkt);
635
39
            return 0;
636
39
        }
637
        /* S2 DTivo has invalid long AC3 packets */
638
31
        if (ty->tivo_series == TIVO_SERIES2) {
639
18
            if (pkt->size > AC3_PKT_LENGTH) {
640
2
                pkt->size -= 2;
641
2
                ty->ac3_pkt_size = 0;
642
16
            } else {
643
16
                ty->ac3_pkt_size = pkt->size;
644
16
            }
645
18
        }
646
462
    } else {
647
        /* Unsupported/Unknown */
648
462
        ty->cur_chunk_pos += rec_size;
649
462
        return 0;
650
462
    }
651
652
2.93k
    return 1;
653
4.19k
}
654
655
static int ty_read_packet(AVFormatContext *s, AVPacket *pkt)
656
11.9k
{
657
11.9k
    TYDemuxContext *ty = s->priv_data;
658
11.9k
    AVIOContext *pb = s->pb;
659
11.9k
    TyRecHdr *rec;
660
11.9k
    int64_t rec_size = 0;
661
11.9k
    int ret = 0;
662
663
11.9k
    if (avio_feof(pb))
664
347
        return AVERROR_EOF;
665
666
183k
    while (ret <= 0) {
667
175k
        if (!ty->rec_hdrs || ty->first_chunk || ty->cur_rec >= ty->num_recs) {
668
1.34k
            if (get_chunk(s) < 0 || ty->num_recs <= 0)
669
196
                return AVERROR_EOF;
670
1.34k
        }
671
672
174k
        rec = &ty->rec_hdrs[ty->cur_rec];
673
174k
        rec_size = rec->rec_size;
674
174k
        ty->cur_rec++;
675
676
174k
        if (rec_size <= 0)
677
159k
            continue;
678
679
15.7k
        if (ty->cur_chunk_pos + rec->rec_size > CHUNK_SIZE)
680
3.15k
            return AVERROR_INVALIDDATA;
681
682
12.5k
        if (avio_feof(pb))
683
80
            return AVERROR_EOF;
684
685
12.4k
        switch (rec->rec_type) {
686
5.28k
        case VIDEO_ID:
687
5.28k
            ret = demux_video(s, rec, pkt);
688
5.28k
            break;
689
4.19k
        case AUDIO_ID:
690
4.19k
            ret = demux_audio(s, rec, pkt);
691
4.19k
            break;
692
2.76k
        default:
693
2.76k
            ff_dlog(s, "Invalid record type 0x%02x\n", rec->rec_type);
694
2.79k
        case 0x01:
695
2.95k
        case 0x02:
696
3.01k
        case 0x03: /* TiVo data services */
697
3.02k
        case 0x05: /* unknown, but seen regularly */
698
3.02k
            ty->cur_chunk_pos += rec->rec_size;
699
3.02k
            break;
700
12.4k
        }
701
12.4k
    }
702
703
8.16k
    return 0;
704
11.5k
}
705
706
static int ty_read_close(AVFormatContext *s)
707
1.16k
{
708
1.16k
    TYDemuxContext *ty = s->priv_data;
709
710
1.16k
    av_freep(&ty->rec_hdrs);
711
712
1.16k
    return 0;
713
1.16k
}
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
};