Browse code

avformat: add TiVo ty demuxer

Signed-off-by: Paul B Mahol <onemda@gmail.com>

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