FFmpeg
ty.c
Go to the documentation of this file.
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];
54  uint64_t ty_pts; /* TY PTS in the record header */
55 } TyRecHdr;
56 
57 typedef enum {
61 } TiVo_type;
62 
63 typedef enum {
67 } TiVo_series;
68 
69 typedef enum {
73 } TiVo_audio;
74 
75 typedef struct TySeqTable {
76  uint64_t timestamp;
78 } TySeqTable;
79 
80 typedef struct TYDemuxContext {
81  unsigned cur_chunk;
82  unsigned cur_chunk_pos;
83  int64_t cur_pos;
84  TiVo_type tivo_type; /* TiVo type (SA / DTiVo) */
85  TiVo_series tivo_series; /* Series1 or Series2 */
86  TiVo_audio audio_type; /* AC3 or MPEG */
87  int pes_length; /* Length of Audio PES header */
88  int pts_offset; /* offset into audio PES of PTS */
89  uint8_t pes_buffer[20]; /* holds incomplete pes headers */
90  int pes_buf_cnt; /* how many bytes in our buffer */
91  size_t ac3_pkt_size; /* length of ac3 pkt we've seen so far */
92  uint64_t last_ty_pts; /* last TY timestamp we've seen */
93  unsigned seq_table_size; /* number of entries in SEQ table */
94 
95  int64_t first_audio_pts;
96  int64_t last_audio_pts;
97  int64_t last_video_pts;
98 
99  TyRecHdr *rec_hdrs; /* record headers array */
100  int cur_rec; /* current record in this chunk */
101  int num_recs; /* number of recs in this chunk */
102  int seq_rec; /* record number where seq start is */
103  TySeqTable *seq_table; /* table of SEQ entries from mstr chk */
105 
108 
109 static int ty_probe(const AVProbeData *p)
110 {
111  int i;
112 
113  for (i = 0; i + 12 < p->buf_size; i += CHUNK_SIZE) {
114  if (AV_RB32(p->buf + i) == TIVO_PES_FILEID &&
115  AV_RB32(p->buf + i + 4) == 0x02 &&
116  AV_RB32(p->buf + i + 8) == CHUNK_SIZE) {
117  return AVPROBE_SCORE_MAX;
118  }
119  }
120 
121  return 0;
122 }
123 
125  int num_recs)
126 {
127  TyRecHdr *hdrs, *rec_hdr;
128  int i;
129 
130  hdrs = av_calloc(num_recs, sizeof(TyRecHdr));
131  if (!hdrs)
132  return NULL;
133 
134  for (i = 0; i < num_recs; i++) {
135  const uint8_t *record_header = buf + (i * 16);
136 
137  rec_hdr = &hdrs[i]; /* for brevity */
138  rec_hdr->rec_type = record_header[3];
139  rec_hdr->subrec_type = record_header[2] & 0x0f;
140  if ((record_header[0] & 0x80) == 0x80) {
141  uint8_t b1, b2;
142 
143  /* marker bit 2 set, so read extended data */
144  b1 = (((record_header[0] & 0x0f) << 4) |
145  ((record_header[1] & 0xf0) >> 4));
146  b2 = (((record_header[1] & 0x0f) << 4) |
147  ((record_header[2] & 0xf0) >> 4));
148 
149  rec_hdr->ex[0] = b1;
150  rec_hdr->ex[1] = b2;
151  rec_hdr->rec_size = 0;
152  rec_hdr->ty_pts = 0;
153  } else {
154  rec_hdr->rec_size = (record_header[0] << 8 |
155  record_header[1]) << 4 |
156  (record_header[2] >> 4);
157  rec_hdr->ty_pts = AV_RB64(&record_header[8]);
158  }
159  }
160  return hdrs;
161 }
162 
163 static int find_es_header(const uint8_t *header,
164  const uint8_t *buffer, int search_len)
165 {
166  int count;
167 
168  for (count = 0; count < search_len; count++) {
169  if (!memcmp(&buffer[count], header, 4))
170  return count;
171  }
172  return -1;
173 }
174 
175 static int analyze_chunk(AVFormatContext *s, const uint8_t *chunk)
176 {
177  TYDemuxContext *ty = s->priv_data;
178  int num_recs, i;
179  TyRecHdr *hdrs;
180  int num_6e0, num_be0, num_9c0, num_3c0;
181 
182  /* skip if it's a Part header */
183  if (AV_RB32(&chunk[0]) == TIVO_PES_FILEID)
184  return 0;
185 
186  /* number of records in chunk (we ignore high order byte;
187  * rarely are there > 256 chunks & we don't need that many anyway) */
188  num_recs = chunk[0];
189  if (num_recs < 5) {
190  /* try again with the next chunk. Sometimes there are dead ones */
191  return 0;
192  }
193 
194  chunk += 4; /* skip past rec count & SEQ bytes */
195  ff_dlog(s, "probe: chunk has %d recs\n", num_recs);
196  hdrs = parse_chunk_headers(chunk, num_recs);
197  if (!hdrs)
198  return AVERROR(ENOMEM);
199 
200  /* scan headers.
201  * 1. check video packets. Presence of 0x6e0 means S1.
202  * No 6e0 but have be0 means S2.
203  * 2. probe for audio 0x9c0 vs 0x3c0 (AC3 vs Mpeg)
204  * If AC-3, then we have DTivo.
205  * If MPEG, search for PTS offset. This will determine SA vs. DTivo.
206  */
207  num_6e0 = num_be0 = num_9c0 = num_3c0 = 0;
208  for (i = 0; i < num_recs; i++) {
209  switch (hdrs[i].subrec_type << 8 | hdrs[i].rec_type) {
210  case 0x6e0:
211  num_6e0++;
212  break;
213  case 0xbe0:
214  num_be0++;
215  break;
216  case 0x3c0:
217  num_3c0++;
218  break;
219  case 0x9c0:
220  num_9c0++;
221  break;
222  }
223  }
224  ff_dlog(s, "probe: chunk has %d 0x6e0 recs, %d 0xbe0 recs.\n",
225  num_6e0, num_be0);
226 
227  /* set up our variables */
228  if (num_6e0 > 0) {
229  ff_dlog(s, "detected Series 1 Tivo\n");
232  } else if (num_be0 > 0) {
233  ff_dlog(s, "detected Series 2 Tivo\n");
236  }
237  if (num_9c0 > 0) {
238  ff_dlog(s, "detected AC-3 Audio (DTivo)\n");
243  } else if (num_3c0 > 0) {
245  ff_dlog(s, "detected MPEG Audio\n");
246  }
247 
248  /* if tivo_type still unknown, we can check PTS location
249  * in MPEG packets to determine tivo_type */
250  if (ty->tivo_type == TIVO_TYPE_UNKNOWN) {
251  uint32_t data_offset = 16 * num_recs;
252 
253  for (i = 0; i < num_recs; i++) {
254  if (data_offset + hdrs[i].rec_size > CHUNK_SIZE)
255  break;
256 
257  if ((hdrs[i].subrec_type << 8 | hdrs[i].rec_type) == 0x3c0 && hdrs[i].rec_size > 15) {
258  /* first make sure we're aligned */
259  int pes_offset = find_es_header(ty_MPEGAudioPacket,
260  &chunk[data_offset], 5);
261  if (pes_offset >= 0) {
262  /* pes found. on SA, PES has hdr data at offset 6, not PTS. */
263  if ((chunk[data_offset + 6 + pes_offset] & 0x80) == 0x80) {
264  /* S1SA or S2(any) Mpeg Audio (PES hdr, not a PTS start) */
265  if (ty->tivo_series == TIVO_SERIES1)
266  ff_dlog(s, "detected Stand-Alone Tivo\n");
267  ty->tivo_type = TIVO_TYPE_SA;
269  } else {
270  if (ty->tivo_series == TIVO_SERIES1)
271  ff_dlog(s, "detected DirecTV Tivo\n");
274  }
275  break;
276  }
277  }
278  data_offset += hdrs[i].rec_size;
279  }
280  }
281  av_free(hdrs);
282 
283  return 0;
284 }
285 
287 {
288  TYDemuxContext *ty = s->priv_data;
289  AVIOContext *pb = s->pb;
290  AVStream *st, *ast;
291  int i, ret = 0;
292 
296 
297  for (i = 0; i < CHUNK_PEEK_COUNT; i++) {
298  avio_read(pb, ty->chunk, CHUNK_SIZE);
299 
300  ret = analyze_chunk(s, ty->chunk);
301  if (ret < 0)
302  return ret;
303  if (ty->tivo_series != TIVO_SERIES_UNKNOWN &&
306  break;
307  }
308 
309  if (ty->tivo_series == TIVO_SERIES_UNKNOWN ||
312  return AVERROR(EIO);
313 
314  st = avformat_new_stream(s, NULL);
315  if (!st)
316  return AVERROR(ENOMEM);
320  avpriv_set_pts_info(st, 64, 1, 90000);
321 
322  ast = avformat_new_stream(s, NULL);
323  if (!ast)
324  return AVERROR(ENOMEM);
326 
327  if (ty->audio_type == TIVO_AUDIO_MPEG) {
330  } else {
332  }
333  avpriv_set_pts_info(ast, 64, 1, 90000);
334 
335  ty->first_chunk = 1;
336 
337  avio_seek(pb, 0, SEEK_SET);
338 
339  return 0;
340 }
341 
342 /* parse a master chunk, filling the SEQ table and other variables.
343  * We assume the stream is currently pointing to it.
344  */
346 {
347  TYDemuxContext *ty = s->priv_data;
348  unsigned map_size; /* size of bitmask, in bytes */
349  unsigned i, j;
350 
351  /* Note that the entries in the SEQ table in the stream may have
352  different sizes depending on the bits per entry. We store them
353  all in the same size structure, so we have to parse them out one
354  by one. If we had a dynamic structure, we could simply read the
355  entire table directly from the stream into memory in place. */
356 
357  /* clear the SEQ table */
358  av_freep(&ty->seq_table);
359 
360  /* parse header info */
361 
362  map_size = AV_RB32(ty->chunk + 20); /* size of bitmask, in bytes */
363  i = AV_RB32(ty->chunk + 28); /* size of SEQ table, in bytes */
364 
365  ty->seq_table_size = i / (8LL + map_size);
366 
367  if (ty->seq_table_size == 0) {
368  ty->seq_table = NULL;
369  return;
370  }
371 
372  /* parse all the entries */
373  ty->seq_table = av_calloc(ty->seq_table_size, sizeof(TySeqTable));
374  if (ty->seq_table == NULL) {
375  ty->seq_table_size = 0;
376  return;
377  }
378 
379  ty->cur_chunk_pos = 32;
380  for (j = 0; j < ty->seq_table_size; j++) {
381  if (ty->cur_chunk_pos >= CHUNK_SIZE - 8)
382  return;
383  ty->seq_table[j].timestamp = AV_RB64(ty->chunk + ty->cur_chunk_pos);
384  ty->cur_chunk_pos += 8;
385  if (map_size > 8) {
386  av_log(s, AV_LOG_ERROR, "Unsupported SEQ bitmap size in master chunk.\n");
387  ty->cur_chunk_pos += map_size;
388  } else {
389  memcpy(ty->seq_table[j].chunk_bitmask, ty->chunk + ty->cur_chunk_pos, map_size);
390  }
391  }
392 }
393 
395 {
396  TYDemuxContext *ty = s->priv_data;
397  AVIOContext *pb = s->pb;
398  int read_size, num_recs;
399 
400  ff_dlog(s, "parsing ty chunk #%d\n", ty->cur_chunk);
401 
402  /* if we have left-over filler space from the last chunk, get that */
403  if (avio_feof(pb))
404  return AVERROR_EOF;
405 
406  /* read the TY packet header */
407  read_size = avio_read(pb, ty->chunk, CHUNK_SIZE);
408  ty->cur_chunk++;
409 
410  if ((read_size < 4) || (AV_RB32(ty->chunk) == 0)) {
411  return AVERROR_EOF;
412  }
413 
414  /* check if it's a PART Header */
415  if (AV_RB32(ty->chunk) == TIVO_PES_FILEID) {
416  parse_master(s); /* parse master chunk */
417  return get_chunk(s);
418  }
419 
420  /* number of records in chunk (8- or 16-bit number) */
421  if (ty->chunk[3] & 0x80) {
422  /* 16 bit rec cnt */
423  ty->num_recs = num_recs = (ty->chunk[1] << 8) + ty->chunk[0];
424  ty->seq_rec = (ty->chunk[3] << 8) + ty->chunk[2];
425  if (ty->seq_rec != 0xffff) {
426  ty->seq_rec &= ~0x8000;
427  }
428  } else {
429  /* 8 bit reclen - TiVo 1.3 format */
430  ty->num_recs = num_recs = ty->chunk[0];
431  ty->seq_rec = ty->chunk[1];
432  }
433  ty->cur_rec = 0;
434  ty->first_chunk = 0;
435 
436  ff_dlog(s, "chunk has %d records\n", num_recs);
437  ty->cur_chunk_pos = 4;
438 
439  av_freep(&ty->rec_hdrs);
440 
441  if (num_recs * 16 >= CHUNK_SIZE - 4)
442  return AVERROR_INVALIDDATA;
443 
444  ty->rec_hdrs = parse_chunk_headers(ty->chunk + 4, num_recs);
445  if (!ty->rec_hdrs)
446  return AVERROR(ENOMEM);
447  ty->cur_chunk_pos += 16 * num_recs;
448 
449  return 0;
450 }
451 
453 {
454  TYDemuxContext *ty = s->priv_data;
455  const int subrec_type = rec_hdr->subrec_type;
456  const int64_t rec_size = rec_hdr->rec_size;
457  int es_offset1;
458  int got_packet = 0;
459 
460  if (subrec_type != 0x02 && subrec_type != 0x0c &&
461  subrec_type != 0x08 && rec_size > 4) {
462  /* get the PTS from this packet if it has one.
463  * on S1, only 0x06 has PES. On S2, however, most all do.
464  * Do NOT Pass the PES Header to the MPEG2 codec */
465  es_offset1 = find_es_header(ty_VideoPacket, ty->chunk + ty->cur_chunk_pos, 5);
466  if (es_offset1 != -1) {
468  ty->chunk + ty->cur_chunk_pos + es_offset1 + VIDEO_PTS_OFFSET);
469  if (subrec_type != 0x06) {
470  /* if we found a PES, and it's not type 6, then we're S2 */
471  /* The packet will have video data (& other headers) so we
472  * chop out the PES header and send the rest */
473  if (rec_size >= VIDEO_PES_LENGTH + es_offset1) {
474  int size = rec_hdr->rec_size - VIDEO_PES_LENGTH - es_offset1;
475 
476  ty->cur_chunk_pos += VIDEO_PES_LENGTH + es_offset1;
477  if (av_new_packet(pkt, size) < 0)
478  return AVERROR(ENOMEM);
479  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, size);
480  ty->cur_chunk_pos += size;
481  pkt->stream_index = 0;
482  got_packet = 1;
483  } else {
484  ff_dlog(s, "video rec type 0x%02x has short PES"
485  " (%"PRId64" bytes)\n", subrec_type, rec_size);
486  /* nuke this block; it's too short, but has PES marker */
487  ty->cur_chunk_pos += rec_size;
488  return 0;
489  }
490  }
491  }
492  }
493 
494  if (subrec_type == 0x06) {
495  /* type 6 (S1 DTivo) has no data, so we're done */
496  ty->cur_chunk_pos += rec_size;
497  return 0;
498  }
499 
500  if (!got_packet) {
501  if (av_new_packet(pkt, rec_size) < 0)
502  return AVERROR(ENOMEM);
503  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
504  ty->cur_chunk_pos += rec_size;
505  pkt->stream_index = 0;
506  got_packet = 1;
507  }
508 
509  /* if it's not a continue blk, then set PTS */
510  if (subrec_type != 0x02) {
511  if (subrec_type == 0x0c && pkt->size >= 6)
512  pkt->data[5] |= 0x08;
513  if (subrec_type == 0x07) {
514  ty->last_ty_pts = rec_hdr->ty_pts;
515  } else {
516  /* yes I know this is a cheap hack. It's the timestamp
517  used for display and skipping fwd/back, so it
518  doesn't have to be accurate to the millisecond.
519  I adjust it here by roughly one 1/30 sec. Yes it
520  will be slightly off for UK streams, but it's OK.
521  */
522  ty->last_ty_pts += 35000000;
523  //ty->last_ty_pts += 33366667;
524  }
525  /* set PTS for this block before we send */
526  if (ty->last_video_pts > AV_NOPTS_VALUE) {
527  pkt->pts = ty->last_video_pts;
528  /* PTS gets used ONCE.
529  * Any subsequent frames we get BEFORE next PES
530  * header will have their PTS computed in the codec */
532  }
533  }
534 
535  return got_packet;
536 }
537 
539  int32_t offset, int32_t rec_len)
540 {
541  TYDemuxContext *ty = s->priv_data;
542 
543  if (offset < 0 || offset + ty->pes_length > rec_len) {
544  /* entire PES header not present */
545  ff_dlog(s, "PES header at %"PRId32" not complete in record. storing.\n", offset);
546  /* save the partial pes header */
547  if (offset < 0) {
548  /* no header found, fake some 00's (this works, believe me) */
549  memset(ty->pes_buffer, 0, 4);
550  ty->pes_buf_cnt = 4;
551  if (rec_len > 4)
552  ff_dlog(s, "PES header not found in record of %"PRId32" bytes!\n", rec_len);
553  return -1;
554  }
555  /* copy the partial pes header we found */
556  memcpy(ty->pes_buffer, pkt->data + offset, rec_len - offset);
557  ty->pes_buf_cnt = rec_len - offset;
558 
559  if (offset > 0) {
560  /* PES Header was found, but not complete, so trim the end of this record */
561  pkt->size -= rec_len - offset;
562  return 1;
563  }
564  return -1; /* partial PES, no audio data */
565  }
566  /* full PES header present, extract PTS */
568  if (ty->first_audio_pts == AV_NOPTS_VALUE)
570  pkt->pts = ty->last_audio_pts;
571  memmove(pkt->data + offset, pkt->data + offset + ty->pes_length, rec_len - ty->pes_length);
572  pkt->size -= ty->pes_length;
573  return 0;
574 }
575 
577 {
578  TYDemuxContext *ty = s->priv_data;
579  const int subrec_type = rec_hdr->subrec_type;
580  const int64_t rec_size = rec_hdr->rec_size;
581  int es_offset1;
582 
583  if (subrec_type == 2) {
584  int need = 0;
585  /* SA or DTiVo Audio Data, no PES (continued block)
586  * ================================================
587  */
588 
589  /* continue PES if previous was incomplete */
590  if (ty->pes_buf_cnt > 0) {
591  need = ty->pes_length - ty->pes_buf_cnt;
592 
593  ff_dlog(s, "continuing PES header\n");
594  /* do we have enough data to complete? */
595  if (need >= rec_size) {
596  /* don't have complete PES hdr; save what we have and return */
597  memcpy(ty->pes_buffer + ty->pes_buf_cnt, ty->chunk + ty->cur_chunk_pos, rec_size);
598  ty->cur_chunk_pos += rec_size;
599  ty->pes_buf_cnt += rec_size;
600  return 0;
601  }
602 
603  /* we have enough; reconstruct this frame with the new hdr */
604  memcpy(ty->pes_buffer + ty->pes_buf_cnt, ty->chunk + ty->cur_chunk_pos, need);
605  ty->cur_chunk_pos += need;
606  /* get the PTS out of this PES header (MPEG or AC3) */
607  if (ty->audio_type == TIVO_AUDIO_MPEG) {
608  es_offset1 = find_es_header(ty_MPEGAudioPacket,
609  ty->pes_buffer, 5);
610  } else {
611  es_offset1 = find_es_header(ty_AC3AudioPacket,
612  ty->pes_buffer, 5);
613  }
614  if (es_offset1 < 0) {
615  ff_dlog(s, "Can't find audio PES header in packet.\n");
616  } else {
618  &ty->pes_buffer[es_offset1 + ty->pts_offset]);
619  pkt->pts = ty->last_audio_pts;
620  }
621  ty->pes_buf_cnt = 0;
622 
623  }
624  if (av_new_packet(pkt, rec_size - need) < 0)
625  return AVERROR(ENOMEM);
626  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size - need);
627  ty->cur_chunk_pos += rec_size - need;
628  pkt->stream_index = 1;
629 
630  /* S2 DTivo has AC3 packets with 2 padding bytes at end. This is
631  * not allowed in the AC3 spec and will cause problems. So here
632  * we try to trim things. */
633  /* Also, S1 DTivo has alternating short / long AC3 packets. That
634  * is, one packet is short (incomplete) and the next packet has
635  * the first one's missing data, plus all of its own. Strange. */
636  if (ty->audio_type == TIVO_AUDIO_AC3 &&
637  ty->tivo_series == TIVO_SERIES2) {
638  if (ty->ac3_pkt_size + pkt->size > AC3_PKT_LENGTH) {
639  pkt->size -= 2;
640  ty->ac3_pkt_size = 0;
641  } else {
642  ty->ac3_pkt_size += pkt->size;
643  }
644  }
645  } else if (subrec_type == 0x03) {
646  if (av_new_packet(pkt, rec_size) < 0)
647  return AVERROR(ENOMEM);
648  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
649  ty->cur_chunk_pos += rec_size;
650  pkt->stream_index = 1;
651  /* MPEG Audio with PES Header, either SA or DTiVo */
652  /* ================================================ */
653  es_offset1 = find_es_header(ty_MPEGAudioPacket, pkt->data, 5);
654 
655  /* SA PES Header, No Audio Data */
656  /* ================================================ */
657  if ((es_offset1 == 0) && (rec_size == 16)) {
659  if (ty->first_audio_pts == AV_NOPTS_VALUE)
662  return 0;
663  }
664  /* DTiVo Audio with PES Header */
665  /* ================================================ */
666 
667  /* Check for complete PES */
668  if (check_sync_pes(s, pkt, es_offset1, rec_size) == -1) {
669  /* partial PES header found, nothing else.
670  * we're done. */
672  return 0;
673  }
674  } else if (subrec_type == 0x04) {
675  /* SA Audio with no PES Header */
676  /* ================================================ */
677  if (av_new_packet(pkt, rec_size) < 0)
678  return AVERROR(ENOMEM);
679  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
680  ty->cur_chunk_pos += rec_size;
681  pkt->stream_index = 1;
682  pkt->pts = ty->last_audio_pts;
683  } else if (subrec_type == 0x09) {
684  if (av_new_packet(pkt, rec_size) < 0)
685  return AVERROR(ENOMEM);
686  memcpy(pkt->data, ty->chunk + ty->cur_chunk_pos, rec_size);
687  ty->cur_chunk_pos += rec_size ;
688  pkt->stream_index = 1;
689 
690  /* DTiVo AC3 Audio Data with PES Header */
691  /* ================================================ */
692  es_offset1 = find_es_header(ty_AC3AudioPacket, pkt->data, 5);
693 
694  /* Check for complete PES */
695  if (check_sync_pes(s, pkt, es_offset1, rec_size) == -1) {
696  /* partial PES header found, nothing else. we're done. */
698  return 0;
699  }
700  /* S2 DTivo has invalid long AC3 packets */
701  if (ty->tivo_series == TIVO_SERIES2) {
702  if (pkt->size > AC3_PKT_LENGTH) {
703  pkt->size -= 2;
704  ty->ac3_pkt_size = 0;
705  } else {
706  ty->ac3_pkt_size = pkt->size;
707  }
708  }
709  } else {
710  /* Unsupported/Unknown */
711  ty->cur_chunk_pos += rec_size;
712  return 0;
713  }
714 
715  return 1;
716 }
717 
719 {
720  TYDemuxContext *ty = s->priv_data;
721  AVIOContext *pb = s->pb;
722  TyRecHdr *rec;
723  int64_t rec_size = 0;
724  int ret = 0;
725 
726  if (avio_feof(pb))
727  return AVERROR_EOF;
728 
729  while (ret <= 0) {
730  if (!ty->rec_hdrs || ty->first_chunk || ty->cur_rec >= ty->num_recs) {
731  if (get_chunk(s) < 0 || ty->num_recs <= 0)
732  return AVERROR_EOF;
733  }
734 
735  rec = &ty->rec_hdrs[ty->cur_rec];
736  rec_size = rec->rec_size;
737  ty->cur_rec++;
738 
739  if (rec_size <= 0)
740  continue;
741 
742  if (ty->cur_chunk_pos + rec->rec_size > CHUNK_SIZE)
743  return AVERROR_INVALIDDATA;
744 
745  if (avio_feof(pb))
746  return AVERROR_EOF;
747 
748  switch (rec->rec_type) {
749  case VIDEO_ID:
750  ret = demux_video(s, rec, pkt);
751  break;
752  case AUDIO_ID:
753  ret = demux_audio(s, rec, pkt);
754  break;
755  default:
756  ff_dlog(s, "Invalid record type 0x%02x\n", rec->rec_type);
757  case 0x01:
758  case 0x02:
759  case 0x03: /* TiVo data services */
760  case 0x05: /* unknown, but seen regularly */
761  ty->cur_chunk_pos += rec->rec_size;
762  break;
763  }
764  }
765 
766  return 0;
767 }
768 
770 {
771  TYDemuxContext *ty = s->priv_data;
772 
773  av_freep(&ty->seq_table);
774  av_freep(&ty->rec_hdrs);
775 
776  return 0;
777 }
778 
780  .name = "ty",
781  .long_name = NULL_IF_CONFIG_SMALL("TiVo TY Stream"),
782  .priv_data_size = sizeof(TYDemuxContext),
783  .read_probe = ty_probe,
787  .extensions = "ty,ty+",
789 };
TySeqTable::chunk_bitmask
uint8_t chunk_bitmask[8]
Definition: ty.c:77
TYDemuxContext::ac3_pkt_size
size_t ac3_pkt_size
Definition: ty.c:91
av_packet_unref
void av_packet_unref(AVPacket *pkt)
Wipe the packet.
Definition: avpacket.c:599
ty_AC3AudioPacket
static const uint8_t ty_AC3AudioPacket[]
Definition: ty.c:43
AV_CODEC_ID_AC3
@ AV_CODEC_ID_AC3
Definition: avcodec.h:567
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
avformat_new_stream
AVStream * avformat_new_stream(AVFormatContext *s, const AVCodec *c)
Add a new stream to a media file.
Definition: utils.c:4480
ty_read_header
static int ty_read_header(AVFormatContext *s)
Definition: ty.c:286
TYDemuxContext::seq_rec
int seq_rec
Definition: ty.c:102
AVCodecParameters::codec_type
enum AVMediaType codec_type
General type of the encoded data.
Definition: avcodec.h:3953
TiVo_audio
TiVo_audio
Definition: ty.c:69
ff_parse_pes_pts
static int64_t ff_parse_pes_pts(const uint8_t *buf)
Parse MPEG-PES five-byte timestamp.
Definition: mpeg.h:68
AVERROR_EOF
#define AVERROR_EOF
End of file.
Definition: error.h:55
find_es_header
static int find_es_header(const uint8_t *header, const uint8_t *buffer, int search_len)
Definition: ty.c:163
count
void INT64 INT64 count
Definition: avisynth_c.h:767
analyze_chunk
static int analyze_chunk(AVFormatContext *s, const uint8_t *chunk)
Definition: ty.c:175
ty_read_packet
static int ty_read_packet(AVFormatContext *s, AVPacket *pkt)
Definition: ty.c:718
ff_ty_demuxer
AVInputFormat ff_ty_demuxer
Definition: ty.c:779
AVPacket::data
uint8_t * data
Definition: avcodec.h:1477
TYDemuxContext::last_audio_pts
int64_t last_audio_pts
Definition: ty.c:96
AUDIO_ID
#define AUDIO_ID
Definition: mpeg.h:41
AVProbeData::buf_size
int buf_size
Size of buf except extra allocated bytes.
Definition: avformat.h:449
AC3_PES_LENGTH
#define AC3_PES_LENGTH
Definition: ty.c:33
CHUNK_SIZE
#define CHUNK_SIZE
Definition: ty.c:46
TIVO_AUDIO_MPEG
@ TIVO_AUDIO_MPEG
Definition: ty.c:72
AVPROBE_SCORE_MAX
#define AVPROBE_SCORE_MAX
maximum score
Definition: avformat.h:458
TIVO_SERIES1
@ TIVO_SERIES1
Definition: ty.c:65
TiVo_type
TiVo_type
Definition: ty.c:57
TyRecHdr
Definition: ty.c:49
read_close
static av_cold int read_close(AVFormatContext *ctx)
Definition: libcdio.c:145
TIVO_AUDIO_AC3
@ TIVO_AUDIO_AC3
Definition: ty.c:71
TYDemuxContext::cur_rec
int cur_rec
Definition: ty.c:100
TYDemuxContext::last_video_pts
int64_t last_video_pts
Definition: ty.c:97
ty_probe
static int ty_probe(const AVProbeData *p)
Definition: ty.c:109
SA_PTS_OFFSET
#define SA_PTS_OFFSET
Definition: ty.c:36
TySeqTable
Definition: ty.c:75
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
buf
void * buf
Definition: avisynth_c.h:766
AVInputFormat
Definition: avformat.h:640
TYDemuxContext::first_chunk
int first_chunk
Definition: ty.c:104
AC3_PKT_LENGTH
#define AC3_PKT_LENGTH
Definition: ty.c:39
intreadwrite.h
s
#define s(width, name)
Definition: cbs_vp9.c:257
CHUNK_PEEK_COUNT
#define CHUNK_PEEK_COUNT
Definition: ty.c:47
av_new_packet
int av_new_packet(AVPacket *pkt, int size)
Allocate the payload of a packet and initialize its fields with default values.
Definition: avpacket.c:86
demux_audio
static int demux_audio(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt)
Definition: ty.c:576
AVInputFormat::name
const char * name
A comma separated list of short names for the format.
Definition: avformat.h:645
VIDEO_PTS_OFFSET
#define VIDEO_PTS_OFFSET
Definition: ty.c:38
TySeqTable::timestamp
uint64_t timestamp
Definition: ty.c:76
AVProbeData::buf
unsigned char * buf
Buffer must have AVPROBE_PADDING_SIZE of extra allocated bytes filled with zero.
Definition: avformat.h:448
AVMEDIA_TYPE_AUDIO
@ AVMEDIA_TYPE_AUDIO
Definition: avutil.h:202
AV_CODEC_ID_MP2
@ AV_CODEC_ID_MP2
Definition: avcodec.h:564
AVStream::need_parsing
enum AVStreamParseType need_parsing
Definition: avformat.h:1088
AC3_PTS_OFFSET
#define AC3_PTS_OFFSET
Definition: ty.c:37
TIVO_TYPE_SA
@ TIVO_TYPE_SA
Definition: ty.c:59
TyRecHdr::subrec_type
uint8_t subrec_type
Definition: ty.c:53
int32_t
int32_t
Definition: audio_convert.c:194
TYDemuxContext::pts_offset
int pts_offset
Definition: ty.c:88
TIVO_PES_FILEID
#define TIVO_PES_FILEID
Definition: ty.c:45
AVFormatContext
Format I/O context.
Definition: avformat.h:1342
need
must be printed separately If there s no standard function for printing the type you need
Definition: tablegen.txt:45
internal.h
AVStream::codecpar
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition: avformat.h:1017
parse_chunk_headers
static TyRecHdr * parse_chunk_headers(const uint8_t *buf, int num_recs)
Definition: ty.c:124
read_header
static int read_header(FFV1Context *f)
Definition: ffv1dec.c:530
NULL
#define NULL
Definition: coverity.c:32
DTIVO_PTS_OFFSET
#define DTIVO_PTS_OFFSET
Definition: ty.c:35
read_probe
static int read_probe(const AVProbeData *pd)
Definition: jvdec.c:55
TYDemuxContext::cur_pos
int64_t cur_pos
Definition: ty.c:83
AVProbeData
This structure contains the data a format has to probe a file.
Definition: avformat.h:446
TyRecHdr::ex
uint8_t ex[2]
Definition: ty.c:51
TyRecHdr::rec_size
int64_t rec_size
Definition: ty.c:50
TYDemuxContext::last_ty_pts
uint64_t last_ty_pts
Definition: ty.c:92
ty_MPEGAudioPacket
static const uint8_t ty_MPEGAudioPacket[]
Definition: ty.c:42
TyRecHdr::ty_pts
uint64_t ty_pts
Definition: ty.c:54
TYDemuxContext::tivo_type
TiVo_type tivo_type
Definition: ty.c:84
ff_dlog
#define ff_dlog(a,...)
Definition: tableprint_vlc.h:29
AVIOContext
Bytestream IO Context.
Definition: avio.h:161
AVPacket::size
int size
Definition: avcodec.h:1478
TYDemuxContext::seq_table
TySeqTable * seq_table
Definition: ty.c:103
NULL_IF_CONFIG_SMALL
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition: internal.h:188
TYDemuxContext::rec_hdrs
TyRecHdr * rec_hdrs
Definition: ty.c:99
avpriv_set_pts_info
void avpriv_set_pts_info(AVStream *s, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den)
Set the time base and wrapping info for a given stream.
Definition: utils.c:4910
size
int size
Definition: twinvq_data.h:11134
AV_NOPTS_VALUE
#define AV_NOPTS_VALUE
Undefined timestamp value.
Definition: avutil.h:248
AV_RB32
uint64_t_TMPL AV_WL64 unsigned int_TMPL AV_WL32 unsigned int_TMPL AV_WL24 unsigned int_TMPL AV_WL16 uint64_t_TMPL AV_WB64 unsigned int_TMPL AV_RB32
Definition: bytestream.h:92
header
static const uint8_t header[24]
Definition: sdr2.c:67
mpeg.h
offset
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf offset
Definition: writing_filters.txt:86
get_chunk
static int get_chunk(AVFormatContext *s)
Definition: ty.c:394
TIVO_AUDIO_UNKNOWN
@ TIVO_AUDIO_UNKNOWN
Definition: ty.c:70
VIDEO_PES_LENGTH
#define VIDEO_PES_LENGTH
Definition: ty.c:34
TiVo_series
TiVo_series
Definition: ty.c:63
TYDemuxContext::pes_buffer
uint8_t pes_buffer[20]
Definition: ty.c:89
i
#define i(width, name, range_min, range_max)
Definition: cbs_h2645.c:259
AVPacket::pts
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...
Definition: avcodec.h:1470
TYDemuxContext::pes_buf_cnt
int pes_buf_cnt
Definition: ty.c:90
parse_master
static void parse_master(AVFormatContext *s)
Definition: ty.c:345
TYDemuxContext::cur_chunk_pos
unsigned cur_chunk_pos
Definition: ty.c:82
TIVO_SERIES_UNKNOWN
@ TIVO_SERIES_UNKNOWN
Definition: ty.c:64
uint8_t
uint8_t
Definition: audio_convert.c:194
SERIES2_PES_LENGTH
#define SERIES2_PES_LENGTH
Definition: ty.c:32
TYDemuxContext::first_audio_pts
int64_t first_audio_pts
Definition: ty.c:95
ret
ret
Definition: filter_design.txt:187
read_packet
static int read_packet(void *opaque, uint8_t *buf, int buf_size)
Definition: avio_reading.c:42
AVStream
Stream structure.
Definition: avformat.h:870
avio_seek
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:246
avformat.h
TYDemuxContext::seq_table_size
unsigned seq_table_size
Definition: ty.c:93
TIVO_TYPE_DTIVO
@ TIVO_TYPE_DTIVO
Definition: ty.c:60
TYDemuxContext::audio_type
TiVo_audio audio_type
Definition: ty.c:86
VIDEO_ID
#define VIDEO_ID
Definition: mpeg.h:42
TYDemuxContext::pes_length
int pes_length
Definition: ty.c:87
av_calloc
void * av_calloc(size_t nmemb, size_t size)
Non-inlined equivalent of av_mallocz_array().
Definition: mem.c:244
pkt
static AVPacket pkt
Definition: demuxing_decoding.c:54
buffer
the frame and frame reference mechanism is intended to as much as expensive copies of that data while still allowing the filters to produce correct results The data is stored in buffers represented by AVFrame structures Several references can point to the same frame buffer
Definition: filter_design.txt:49
avio_read
int avio_read(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition: aviobuf.c:647
AVSTREAM_PARSE_FULL_RAW
@ AVSTREAM_PARSE_FULL_RAW
full parsing and repack with timestamp and position generation by parser for raw this assumes that ea...
Definition: avformat.h:795
AVPacket::stream_index
int stream_index
Definition: avcodec.h:1479
AVMEDIA_TYPE_VIDEO
@ AVMEDIA_TYPE_VIDEO
Definition: avutil.h:201
AVFMT_TS_DISCONT
#define AVFMT_TS_DISCONT
Format allows timestamp discontinuities.
Definition: avformat.h:469
demux_video
static int demux_video(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt)
Definition: ty.c:452
TYDemuxContext::num_recs
int num_recs
Definition: ty.c:101
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
AVCodecParameters::codec_id
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition: avcodec.h:3957
TYDemuxContext
Definition: ty.c:80
AVPacket
This structure stores compressed data.
Definition: avcodec.h:1454
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
TYDemuxContext::cur_chunk
unsigned cur_chunk
Definition: ty.c:81
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:565
SERIES1_PES_LENGTH
#define SERIES1_PES_LENGTH
Definition: ty.c:31
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:28
check_sync_pes
static int check_sync_pes(AVFormatContext *s, AVPacket *pkt, int32_t offset, int32_t rec_len)
Definition: ty.c:538
TYDemuxContext::tivo_series
TiVo_series tivo_series
Definition: ty.c:85
AVERROR_INVALIDDATA
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
ty_VideoPacket
static const uint8_t ty_VideoPacket[]
Definition: ty.c:41
TIVO_TYPE_UNKNOWN
@ TIVO_TYPE_UNKNOWN
Definition: ty.c:58
TYDemuxContext::chunk
uint8_t chunk[CHUNK_SIZE]
Definition: ty.c:106
ty_read_close
static int ty_read_close(AVFormatContext *s)
Definition: ty.c:769
TIVO_SERIES2
@ TIVO_SERIES2
Definition: ty.c:66
AV_CODEC_ID_MPEG2VIDEO
@ AV_CODEC_ID_MPEG2VIDEO
preferred ID for MPEG-1/2 video decoding
Definition: avcodec.h:220
AV_RB64
uint64_t_TMPL AV_WL64 unsigned int_TMPL AV_WL32 unsigned int_TMPL AV_WL24 unsigned int_TMPL AV_WL16 uint64_t_TMPL AV_RB64
Definition: bytestream.h:91
TyRecHdr::rec_type
uint8_t rec_type
Definition: ty.c:52
avio_feof
int avio_feof(AVIOContext *s)
Similar to feof() but also returns nonzero on read errors.
Definition: aviobuf.c:358