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];
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 {
61 } TiVo_type;
62 
63 typedef enum {
67 } TiVo_series;
68 
69 typedef enum {
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 */
97 
98  uint8_t chunk[CHUNK_SIZE];
100 
101 static int ty_probe(const AVProbeData *p)
102 {
103  int i;
104 
105  for (i = 0; i + 12 < p->buf_size; i += CHUNK_SIZE) {
106  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  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");
224  } else if (num_be0 > 0) {
225  ff_dlog(s, "detected Series 2 Tivo\n");
228  }
229  if (num_9c0 > 0) {
230  ff_dlog(s, "detected AC-3 Audio (DTivo)\n");
235  } else if (num_3c0 > 0) {
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;
261  } else {
262  if (ty->tivo_series == TIVO_SERIES1)
263  ff_dlog(s, "detected DirecTV Tivo\n");
266  }
267  break;
268  }
269  }
270  data_offset += hdrs[i].rec_size;
271  }
272  }
273  av_free(hdrs);
274 
275  return 0;
276 }
277 
279 {
280  TYDemuxContext *ty = s->priv_data;
281  AVIOContext *pb = s->pb;
282  AVStream *st, *ast;
283  int i, ret = 0;
284 
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 &&
298  break;
299  }
300 
301  if (ty->tivo_series == TIVO_SERIES_UNKNOWN ||
304  return AVERROR(EIO);
305 
306  st = avformat_new_stream(s, NULL);
307  if (!st)
308  return AVERROR(ENOMEM);
312  avpriv_set_pts_info(st, 64, 1, 90000);
313 
314  ast = avformat_new_stream(s, NULL);
315  if (!ast)
316  return AVERROR(ENOMEM);
318 
319  if (ty->audio_type == TIVO_AUDIO_MPEG) {
322  } else {
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 
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 
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) {
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 */
467  }
468  }
469 
470  return got_packet;
471 }
472 
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 */
503  if (ty->first_audio_pts == AV_NOPTS_VALUE)
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 
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 {
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)) {
594  if (ty->first_audio_pts == AV_NOPTS_VALUE)
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. */
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. */
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 
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 
705 {
706  TYDemuxContext *ty = s->priv_data;
707 
708  av_freep(&ty->rec_hdrs);
709 
710  return 0;
711 }
712 
714  .name = "ty",
715  .long_name = NULL_IF_CONFIG_SMALL("TiVo TY Stream"),
716  .priv_data_size = sizeof(TYDemuxContext),
717  .read_probe = ty_probe,
721  .extensions = "ty,ty+",
723 };
TYDemuxContext::ac3_pkt_size
size_t ac3_pkt_size
Definition: ty.c:86
av_packet_unref
void av_packet_unref(AVPacket *pkt)
Wipe the packet.
Definition: avpacket.c:417
ty_AC3AudioPacket
static const uint8_t ty_AC3AudioPacket[]
Definition: ty.c:43
AV_CODEC_ID_AC3
@ AV_CODEC_ID_AC3
Definition: codec_id.h:425
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:724
ty_read_header
static int ty_read_header(AVFormatContext *s)
Definition: ty.c:278
AVCodecParameters::codec_type
enum AVMediaType codec_type
General type of the encoded data.
Definition: codec_par.h:56
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:57
find_es_header
static int find_es_header(const uint8_t *header, const uint8_t *buffer, int search_len)
Definition: ty.c:155
analyze_chunk
static int analyze_chunk(AVFormatContext *s, const uint8_t *chunk)
Definition: ty.c:167
ty_read_packet
static int ty_read_packet(AVFormatContext *s, AVPacket *pkt)
Definition: ty.c:653
AVPacket::data
uint8_t * data
Definition: packet.h:373
TYDemuxContext::last_audio_pts
int64_t last_audio_pts
Definition: ty.c:90
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
b1
static double b1(void *priv, double x, double y)
Definition: vf_xfade.c:1708
TyRecHdr
Definition: ty.c:49
ffstream
static av_always_inline FFStream * ffstream(AVStream *st)
Definition: internal.h:426
read_close
static av_cold int read_close(AVFormatContext *ctx)
Definition: libcdio.c:141
TIVO_AUDIO_AC3
@ TIVO_AUDIO_AC3
Definition: ty.c:71
TYDemuxContext::cur_rec
int cur_rec
Definition: ty.c:94
TYDemuxContext::last_video_pts
int64_t last_video_pts
Definition: ty.c:91
ty_probe
static int ty_probe(const AVProbeData *p)
Definition: ty.c:101
SA_PTS_OFFSET
#define SA_PTS_OFFSET
Definition: ty.c:36
pkt
AVPacket * pkt
Definition: movenc.c:59
AVInputFormat
Definition: avformat.h:626
TYDemuxContext::first_chunk
int first_chunk
Definition: ty.c:96
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:99
demux_audio
static int demux_audio(AVFormatContext *s, TyRecHdr *rec_hdr, AVPacket *pkt)
Definition: ty.c:511
AVInputFormat::name
const char * name
A comma separated list of short names for the format.
Definition: avformat.h:631
VIDEO_PTS_OFFSET
#define VIDEO_PTS_OFFSET
Definition: ty.c:38
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: codec_id.h:422
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
TYDemuxContext::pts_offset
int pts_offset
Definition: ty.c:83
TIVO_PES_FILEID
#define TIVO_PES_FILEID
Definition: ty.c:45
FFStream::need_parsing
enum AVStreamParseType need_parsing
Definition: internal.h:399
AVFormatContext
Format I/O context.
Definition: avformat.h:1097
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:992
parse_chunk_headers
static TyRecHdr * parse_chunk_headers(const uint8_t *buf, int num_recs)
Definition: ty.c:116
read_header
static int read_header(FFV1Context *f)
Definition: ffv1dec.c:527
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:78
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:87
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:79
ff_dlog
#define ff_dlog(a,...)
Definition: tableprint_vlc.h:29
AVIOContext
Bytestream IO Context.
Definition: avio.h:161
AVPacket::size
int size
Definition: packet.h:374
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:117
TYDemuxContext::rec_hdrs
TyRecHdr * rec_hdrs
Definition: ty.c:93
size
int size
Definition: twinvq_data.h:10344
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:96
header
static const uint8_t header[24]
Definition: sdr2.c:67
b2
static double b2(void *priv, double x, double y)
Definition: vf_xfade.c:1709
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:334
TIVO_AUDIO_UNKNOWN
@ TIVO_AUDIO_UNKNOWN
Definition: ty.c:70
VIDEO_PES_LENGTH
#define VIDEO_PES_LENGTH
Definition: ty.c:34
i
int i
Definition: input.c:406
TiVo_series
TiVo_series
Definition: ty.c:63
TYDemuxContext::pes_buffer
uint8_t pes_buffer[20]
Definition: ty.c:84
AVPacket::pts
int64_t pts
Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will b...
Definition: packet.h:366
TYDemuxContext::pes_buf_cnt
int pes_buf_cnt
Definition: ty.c:85
TYDemuxContext::cur_chunk_pos
unsigned cur_chunk_pos
Definition: ty.c:77
TIVO_SERIES_UNKNOWN
@ TIVO_SERIES_UNKNOWN
Definition: ty.c:64
SERIES2_PES_LENGTH
#define SERIES2_PES_LENGTH
Definition: ty.c:32
TYDemuxContext::first_audio_pts
int64_t first_audio_pts
Definition: ty.c:89
av_calloc
void * av_calloc(size_t nmemb, size_t size)
Definition: mem.c:271
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:845
avio_seek
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition: aviobuf.c:246
avformat.h
TIVO_TYPE_DTIVO
@ TIVO_TYPE_DTIVO
Definition: ty.c:60
TYDemuxContext::audio_type
TiVo_audio audio_type
Definition: ty.c:81
VIDEO_ID
#define VIDEO_ID
Definition: mpeg.h:42
TYDemuxContext::pes_length
int pes_length
Definition: ty.c:82
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:625
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:772
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: utils.c:1146
AVPacket::stream_index
int stream_index
Definition: packet.h:375
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:387
TYDemuxContext::num_recs
int num_recs
Definition: ty.c:95
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:60
TYDemuxContext
Definition: ty.c:75
AVPacket
This structure stores compressed data.
Definition: packet.h:350
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
int32_t
int32_t
Definition: audioconvert.c:56
TYDemuxContext::cur_chunk
unsigned cur_chunk
Definition: ty.c:76
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:561
SERIES1_PES_LENGTH
#define SERIES1_PES_LENGTH
Definition: ty.c:31
check_sync_pes
static int check_sync_pes(AVFormatContext *s, AVPacket *pkt, int32_t offset, int32_t rec_len)
Definition: ty.c:473
TYDemuxContext::tivo_series
TiVo_series tivo_series
Definition: ty.c:80
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:41
TIVO_TYPE_UNKNOWN
@ TIVO_TYPE_UNKNOWN
Definition: ty.c:58
TYDemuxContext::chunk
uint8_t chunk[CHUNK_SIZE]
Definition: ty.c:98
ty_read_close
static int ty_read_close(AVFormatContext *s)
Definition: ty.c:704
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: codec_id.h:52
ff_ty_demuxer
const AVInputFormat ff_ty_demuxer
Definition: ty.c:713
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:52
avio_feof
int avio_feof(AVIOContext *s)
Similar to feof() but also returns nonzero on read errors.
Definition: aviobuf.c:360