FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/ty.c
Date: 2024-02-29 09:57:37
Exec Total Coverage
Lines: 4 332 1.2%
Functions: 1 11 9.1%
Branches: 3 199 1.5%

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