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