diff -ruN ./common.mak /tmp/mythtv/mythtv/external/FFmpeg/common.mak --- ./common.mak 2014-08-09 07:27:10.186281324 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/common.mak 2014-08-09 07:18:01.470293204 +0200 @@ -94,7 +94,7 @@ TESTPROGS += $(TESTPROGS-yes) LDLIBS = $(FFLIBS:%=%$(BUILDSUF)) -FFEXTRALIBS := $(LDLIBS:%=$(LD_LIB)) $(EXTRALIBS) +FFEXTRALIBS := $(LDLIBS:%=$(LD_MYTH_LIB)) $(EXTRALIBS) OBJS := $(sort $(OBJS:%=$(SUBDIR)%)) SLIBOBJS := $(sort $(SLIBOBJS:%=$(SUBDIR)%)) diff -ruN ./configure /tmp/mythtv/mythtv/external/FFmpeg/configure --- ./configure 2014-08-09 07:27:10.290281322 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/configure 2014-08-09 07:18:01.470293204 +0200 @@ -1980,8 +1980,8 @@ mp3on4float_decoder_select="mpegaudio" mpc7_decoder_select="dsputil mpegaudiodsp" mpc8_decoder_select="dsputil mpegaudiodsp" -mpeg_xvmc_decoder_deps="X11_extensions_XvMClib_h" -mpeg_xvmc_decoder_select="mpeg2video_decoder" +#MYTH mpeg_xvmc_decoder_deps="X11_extensions_XvMClib_h" +#MYTH mpeg_xvmc_decoder_select="mpeg2video_decoder" mpeg1video_decoder_select="error_resilience mpegvideo" mpeg1video_encoder_select="aandcttables mpegvideoenc h263dsp" mpeg2video_decoder_select="error_resilience mpegvideo" @@ -2534,6 +2534,7 @@ CXX_O='-o $@' LD_O='-o $@' LD_LIB='-l%' +LD_MYTH_LIB='-lmyth%' LD_PATH='-L' HOSTCC_C='-c' HOSTCC_E='-E -o $@' @@ -3795,12 +3796,14 @@ if enabled shared; then # Link to the import library instead of the normal static library # for shared libs. - LD_LIB='%.lib' + LD_LIB='myth%.lib' + LD_MYTH_LIB='myth%.lib' # Cannot build both shared and static libs with MSVC or icl. disable static fi shlibdir_default="$bindir_default" - SLIBPREF="" + LIBPREF="myth" + SLIBPREF="myth" SLIBSUF=".dll" SLIBNAME_WITH_VERSION='$(SLIBPREF)$(FULLNAME)-$(LIBVERSION)$(SLIBSUF)' SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)' @@ -4402,7 +4405,7 @@ check_header vdpau/vdpau_x11.h check_header VideoDecodeAcceleration/VDADecoder.h check_header windows.h -check_header X11/extensions/XvMClib.h +#MYTH check_header X11/extensions/XvMClib.h check_header asm/types.h if ! disabled w32threads && ! enabled pthreads; then @@ -5084,6 +5087,7 @@ CXX_O=$CXX_O LD_O=$LD_O LD_LIB=$LD_LIB +LD_MYTH_LIB=$LD_MYTH_LIB LD_PATH=$LD_PATH DLLTOOL=$dlltool WINDRES=$windres diff -ruN ./.gitignore /tmp/mythtv/mythtv/external/FFmpeg/.gitignore --- ./.gitignore 2014-08-09 07:27:13.702281248 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/.gitignore 2014-08-09 07:18:01.466293204 +0200 @@ -81,3 +81,5 @@ /tools/trasher /tools/seek_print /tools/zmqsend +/mythffprobe +/mythffserver diff -ruN ./libavcodec/avcodec.h /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/avcodec.h --- ./libavcodec/avcodec.h 2014-08-09 07:27:13.882281244 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/avcodec.h 2014-08-09 07:18:01.522293203 +0200 @@ -505,6 +505,13 @@ AV_CODEC_ID_PJS = MKBETAG('P','h','J','S'), AV_CODEC_ID_ASS = MKBETAG('A','S','S',' '), ///< ASS as defined in Matroska + /* teletext codecs */ + AV_CODEC_ID_MPEG2VBI, + AV_CODEC_ID_DVB_VBI, + + /* DSMCC codec */ + AV_CODEC_ID_DSMCC_B, + /* other specific kind of codecs (generally used for attachments) */ AV_CODEC_ID_FIRST_UNKNOWN = 0x18000, ///< A dummy ID pointing at the start of various fake codecs. AV_CODEC_ID_TTF = 0x18000, @@ -1231,6 +1238,15 @@ */ unsigned int stream_codec_tag; +#if 1 || FF_API_SUB_ID + /** + * this field is unused by ffmpeg, but is used by MythTV for the DVB + * caption carousel + * + */ + int sub_id; +#endif + void *priv_data; /** @@ -2203,6 +2219,15 @@ */ int refcounted_frames; + /** + * set when bilingual audio data has been detected. + * 0 normally, 1 if dual language flag is set + * + * - encoding: unused (called delay in this case...) + * - decoding: set by lavc + */ + int avcodec_dual_language; + /* - encoding parameters */ float qcompress; ///< amount of qscale change between easy & hard scenes (0.0-1.0) float qblur; ///< amount of qscale smoothing over time (0.0-1.0) @@ -3276,6 +3301,10 @@ int w; ///< width of pict, undefined when pict is not set int h; ///< height of pict, undefined when pict is not set int nb_colors; ///< number of colors in pict, undefined when pict is not set + int display_x; ///< top left corner of region into which pict is displayed + int display_y; ///< top left corner of region into which pict is displayed + int display_w; ///< width of region into which pict is displayed + int display_h; ///< height of region into which pict is displayed /** * data+linesize for the bitmap of this subtitle. @@ -3303,6 +3332,7 @@ unsigned num_rects; AVSubtitleRect **rects; int64_t pts; ///< Same as packet pts, in AV_TIME_BASE + int forced; } AVSubtitle; /** @@ -5089,6 +5119,10 @@ */ const AVCodecDescriptor *avcodec_descriptor_get_by_name(const char *name); +const char *ff_codec_id_string(enum AVCodecID codec_id); +const char *ff_codec_type_string(enum AVMediaType codec_type); +const uint8_t *avpriv_find_start_code(const uint8_t *p, const uint8_t *end, uint32_t *state); + /** * @} */ diff -ruN ./libavcodec/codec_desc.c /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/codec_desc.c --- ./libavcodec/codec_desc.c 2014-08-09 07:27:13.878281244 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/codec_desc.c 2014-08-09 07:18:01.534293202 +0200 @@ -2630,6 +2630,18 @@ .long_name = NULL_IF_CONFIG_SMALL("DVD Nav packet"), }, { + .id = AV_CODEC_ID_DSMCC_B, + .type = AVMEDIA_TYPE_DATA, + .name = "dsmcc_b", + .long_name = NULL_IF_CONFIG_SMALL("DSMCC B"), + }, + { + .id = AV_CODEC_ID_DVD_NAV, + .type = AVMEDIA_TYPE_DATA, + .name = "dvd_nav_packet", + .long_name = NULL_IF_CONFIG_SMALL("DVD Nav packet"), + }, + { .id = AV_CODEC_ID_TIMED_ID3, .type = AVMEDIA_TYPE_DATA, .name = "timed_id3", diff -ruN ./libavcodec/dvbsubdec.c /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/dvbsubdec.c --- ./libavcodec/dvbsubdec.c 2014-08-09 07:27:13.886281244 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/dvbsubdec.c 2014-08-09 07:18:01.546293202 +0200 @@ -819,6 +819,11 @@ else map_table = NULL; + if (y_pos >= region->height) { + av_log(avctx, AV_LOG_ERROR, "Invalid y position!\n"); + return; + } + x_pos = dvbsub_read_2bit_string(pbuf + (y_pos * region->width), region->width, &buf, buf_end - buf, non_mod, map_table, x_pos); @@ -834,6 +839,11 @@ else map_table = NULL; + if (y_pos >= region->height) { + av_log(avctx, AV_LOG_ERROR, "Invalid y position!\n"); + return; + } + x_pos = dvbsub_read_4bit_string(pbuf + (y_pos * region->width), region->width, &buf, buf_end - buf, non_mod, map_table, x_pos); @@ -844,6 +854,11 @@ return; } + if (y_pos >= region->height) { + av_log(avctx, AV_LOG_ERROR, "Invalid y position!\n"); + return; + } + x_pos = dvbsub_read_8bit_string(pbuf + (y_pos * region->width), region->width, &buf, buf_end - buf, non_mod, NULL, x_pos); @@ -1019,6 +1034,11 @@ return AVERROR_INVALIDDATA; } + if (alpha == 255) + { + r = g = b = 0; + } + if (depth & 0x80) clut->clut4[entry_id] = RGBA(r,g,b,255 - alpha); else if (depth & 0x40) @@ -1376,15 +1396,9 @@ DVBSubCLUT *clut; uint32_t *clut_table; int i; - int offset_x=0, offset_y=0; sub->end_display_time = ctx->time_out * 1000; - if (display_def) { - offset_x = display_def->x; - offset_y = display_def->y; - } - sub->num_rects = 0; for (display = ctx->display_list; display; display = display->next) { @@ -1410,10 +1424,20 @@ continue; rect = sub->rects[i]; - rect->x = display->x_pos + offset_x; - rect->y = display->y_pos + offset_y; + rect->x = display->x_pos; + rect->y = display->y_pos; rect->w = region->width; rect->h = region->height; + if (display_def) { + rect->display_x = display_def->x; + rect->display_y = display_def->y; + rect->display_w = display_def->width; + rect->display_h = display_def->height; + } + else { + rect->display_w = 720; + rect->display_h = 576; + } rect->nb_colors = (1 << region->depth); rect->type = SUBTITLE_BITMAP; rect->pict.linesize[0] = region->width; @@ -1487,6 +1511,7 @@ p = buf; p_end = buf + buf_size; + int gotpage, gotregion, gotclut, gotobject, gotdisplay = 0; while (p_end - p >= 6 && *p == 0x0f) { p += 1; segment_type = *p++; @@ -1506,19 +1531,23 @@ case DVBSUB_PAGE_SEGMENT: dvbsub_parse_page_segment(avctx, p, segment_length); got_segment |= 1; + gotpage = 1; break; case DVBSUB_REGION_SEGMENT: dvbsub_parse_region_segment(avctx, p, segment_length); got_segment |= 2; + gotregion = 1; break; case DVBSUB_CLUT_SEGMENT: ret = dvbsub_parse_clut_segment(avctx, p, segment_length); if (ret < 0) return ret; got_segment |= 4; + gotclut = 1; break; case DVBSUB_OBJECT_SEGMENT: dvbsub_parse_object_segment(avctx, p, segment_length); got_segment |= 8; + gotobject = 1; break; case DVBSUB_DISPLAYDEFINITION_SEGMENT: dvbsub_parse_display_definition_segment(avctx, p, segment_length); @@ -1526,6 +1555,7 @@ case DVBSUB_DISPLAY_SEGMENT: *data_size = dvbsub_display_end_segment(avctx, p, segment_length, sub); got_segment |= 16; + gotdisplay = 1; break; default: av_dlog(avctx, "Subtitling segment type 0x%x, page id %d, length %d\n", @@ -1541,6 +1571,11 @@ if (got_segment == 15 && sub) *data_size = dvbsub_display_end_segment(avctx, p, 0, sub); + // Some streams do not send a display segment but if we have all the other + // segments then we need no further data. see #9373 + if ((gotpage & gotregion & gotclut & gotobject) && !gotdisplay && sub) + *data_size = dvbsub_display_end_segment(avctx, p, 0, sub); + return p - buf; } diff -ruN ./libavcodec/dvbsub_parser.c /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/dvbsub_parser.c --- ./libavcodec/dvbsub_parser.c 2014-08-09 07:27:13.854281245 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/dvbsub_parser.c 2014-08-09 07:18:01.546293202 +0200 @@ -126,7 +126,11 @@ { len = AV_RB16(p + 4); - if (p + len + 6 <= p_end) + // MythTV #5978 "<=" -> "<" + // This is unresolved ffmpeg issue 378, use + // their solution if/when this is fixed upstream. + //if (p + len + 6 <= p_end) + if (p + len + 6 < p_end) { *poutbuf_size += len + 6; diff -ruN ./libavcodec/dvdsubdec.c /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/dvdsubdec.c --- ./libavcodec/dvdsubdec.c 2014-08-09 07:27:13.850281245 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/dvdsubdec.c 2014-08-09 07:18:01.546293202 +0200 @@ -369,7 +369,7 @@ } else { sub_header->rects[0]->nb_colors = 4; guess_palette(ctx, (uint32_t*)sub_header->rects[0]->pict.data[1], - 0xffff00); + 0xffffff); } sub_header->rects[0]->x = x1; sub_header->rects[0]->y = y1; @@ -389,7 +389,10 @@ cmd_pos = next_cmd_pos; } if (sub_header->num_rects > 0) + { + sub_header->forced = is_menu; return is_menu; + } fail: reset_rects(sub_header); return -1; diff -ruN ./libavcodec/get_bits.h /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/get_bits.h --- ./libavcodec/get_bits.h 2014-08-09 07:27:13.878281244 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/get_bits.h 2014-08-09 07:18:01.562293202 +0200 @@ -138,7 +138,11 @@ #define HAVE_BITS_REMAINING(name, gb) name ## _index < name ## _size_plus8 #endif -#define CLOSE_READER(name, gb) (gb)->index = name ## _index +// Added the void use of the cache to defeat compiler warnings with newer gcc +// (warning: variable 're_cache" set but not used) +# define CLOSE_READER(name, gb) \ + (gb)->index = name##_index; \ + (void)name##_cache # ifdef LONG_BITSTREAM_READER diff -ruN ./libavcodec/.gitignore /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/.gitignore --- ./libavcodec/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/.gitignore 2014-08-09 07:18:01.490293203 +0200 @@ -0,0 +1 @@ +codec_names.h diff -ruN ./libavcodec/golomb.h /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/golomb.h --- ./libavcodec/golomb.h 2014-08-09 07:27:13.858281245 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/golomb.h 2014-08-09 07:18:01.562293202 +0200 @@ -337,7 +337,7 @@ } else { int i; for (i = 0; i < limit && SHOW_UBITS(re, gb, 1) == 0; i++) { - if (gb->size_in_bits <= re_index) + if (gb->size_in_bits <= (int)re_index) return -1; LAST_SKIP_BITS(re, gb, 1); UPDATE_CACHE(re, gb); diff -ruN ./libavcodec/h264_refs.c /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/h264_refs.c --- ./libavcodec/h264_refs.c 2014-08-09 07:27:13.874281245 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/h264_refs.c 2014-08-09 07:18:01.570293202 +0200 @@ -724,6 +724,14 @@ h->short_ref[0] = h->cur_pic_ptr; h->short_ref_count++; h->cur_pic_ptr->reference |= h->picture_structure; + + /* MythTV changes - begin */ + // do not add more reference frames than allowed after seeing frame num gap + if (!mmco_count && h->short_ref_count > h->sps.ref_frame_count) { + pic = h->short_ref[h->short_ref_count - 1]; + remove_short(h, pic->frame_num, 0); + } + /* MythTV changes - end */ } } diff -ruN ./libavcodec/libavcodec.v /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/libavcodec.v --- ./libavcodec/libavcodec.v 2014-08-09 07:27:13.874281245 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/libavcodec.v 2014-08-09 07:18:01.586293201 +0200 @@ -27,5 +27,12 @@ ff_jpeg_fdct*; ff_dnxhd_get_cid_table; ff_dnxhd_cid_table; + ff_ue_golomb_vlc_code; + ff_golomb_vlc_len; + ff_se_golomb_vlc_code; + ff_codec_type_string; + ff_codec_id_string; + ff_zigzag_direct; + avpriv_find_start_code; local: *; }; diff -ruN ./libavcodec/Makefile /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/Makefile --- ./libavcodec/Makefile 2014-08-09 07:27:13.882281244 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/Makefile 2014-08-09 07:18:01.494293203 +0200 @@ -29,6 +29,7 @@ resample.o \ resample2.o \ utils.o \ + utils-mythtv.o \ # parts needed for many different codecs OBJS-$(CONFIG_AANDCTTABLES) += aandcttab.o diff -ruN ./libavcodec/mpeg12dec.c /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpeg12dec.c --- ./libavcodec/mpeg12dec.c 2014-08-09 07:27:13.862281245 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpeg12dec.c 2014-08-09 07:18:01.610293201 +0200 @@ -2273,6 +2273,191 @@ avctx->dtg_active_format = p[0] & 0x0f; } } else if (buf_end - p >= 6 && + p[0] == 0x43 && p[1] == 0x43 && p[2] == 0x01 && p[3] == 0xf8 && + p[4] == 0x9e) { +#undef fprintf + Mpeg1Context *s1 = avctx->priv_data; + MpegEncContext *s = &s1->mpeg_enc_ctx; + int atsc_cnt_loc = s->tmp_atsc_cc_len; + uint8_t real_count = 0; + unsigned int i; + + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = 0x40 | (0x1f&real_count); + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = 0x00; // em_data + + for (i=5; i < (buf_end - p - 2) && + (s->tmp_atsc_cc_len + 3) < ATSC_CC_BUF_SIZE; i++) + { + if ((p[i]&0xfe) == 0xfe) // CC1&CC2 || CC3&CC4 + { + uint8_t type = (p[i] & 0x01) ^ 0x01; + uint8_t cc_data_1 = p[++i]; + uint8_t cc_data_2 = p[++i]; + uint8_t valid = 1; + uint8_t cc608_hdr = 0xf8 | (valid ? 0x04 : 0x00) | type; + real_count++; + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = cc608_hdr; + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = cc_data_1; + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = cc_data_2; + continue; + } + break; + } + if (!real_count) + { + s->tmp_atsc_cc_len = atsc_cnt_loc; + } + else + { + s->tmp_atsc_cc_buf[atsc_cnt_loc] = 0x40 | (0x1f&real_count); + s->tmp_atsc_cc_len = atsc_cnt_loc + 2 + 3 * real_count; + } + } else if (buf_end - p >= 6 && + p[0] == 'G' && p[1] == 'A' && p[2] == '9' && p[3] == '4') { + /* Parse CEA-708/608 Closed Captions in ATSC user data */ + int user_data_type_code = p[4]; + if (user_data_type_code == 0x03) { // caption data + Mpeg1Context *s1 = avctx->priv_data; + MpegEncContext *s = &s1->mpeg_enc_ctx; + int cccnt = p[5] & 0x1f; + int cclen = 3 * cccnt + 2; + int proc = (p[5] >> 6) & 1; + int blen = s->tmp_atsc_cc_len; + + p += 5; + + if ((cclen <= buf_end - p) && ((cclen + blen) < ATSC_CC_BUF_SIZE)) { + uint8_t *dst = s->tmp_atsc_cc_buf + s->tmp_atsc_cc_len; + memcpy(dst, p, cclen); + s->tmp_atsc_cc_len += cclen; + } + } + else if (user_data_type_code == 0x04) { + // additional CEA-608 data, as per SCTE 21 + } + else if (user_data_type_code == 0x05) { + // luma PAM data, as per SCTE 21 + } + else if (user_data_type_code == 0x06) { + // bar data (letterboxing info) + } + } else if (buf_end - p >= 3 && p[0] == 0x03 && ((p[1]&0x7f) == 0x01)) { + // SCTE 20 encoding of CEA-608 + unsigned int cc_count = p[2]>>3; + unsigned int cc_bits = cc_count * 26; + unsigned int cc_bytes = (cc_bits + 7 - 3) / 8; + Mpeg1Context *s1 = avctx->priv_data; + MpegEncContext *s = &s1->mpeg_enc_ctx; + if (buf_end - p >= (2+cc_bytes) && (s->tmp_scte_cc_len + 2 + 3*cc_count) < SCTE_CC_BUF_SIZE) { + int scte_cnt_loc = s->tmp_scte_cc_len; + uint8_t real_count = 0, marker = 1, i; + GetBitContext gb; + init_get_bits(&gb, p+2, (buf_end-p-2) * sizeof(uint8_t)); + get_bits(&gb, 5); // swallow cc_count + s->tmp_scte_cc_buf[s->tmp_scte_cc_len++] = 0x40 | (0x1f&cc_count); + s->tmp_scte_cc_buf[s->tmp_scte_cc_len++] = 0x00; // em_data + for (i = 0; i < cc_count; i++) { + uint8_t valid, cc608_hdr; + uint8_t priority = get_bits(&gb, 2); + uint8_t field_no = get_bits(&gb, 2); + uint8_t line_offset = get_bits(&gb, 5); + uint8_t cc_data_1 = av_reverse[get_bits(&gb, 8)]; + uint8_t cc_data_2 = av_reverse[get_bits(&gb, 8)]; + uint8_t type = (2 == field_no) ? 0x01 : 0x00; + if (!s->top_field_first) + type ^= 0x01; + (void) priority; // we use all the data, don't need priority + marker &= get_bits(&gb, 1); + // dump if marker bit missing + valid = marker; + // ignore forbidden field numbers + valid = valid && (0 != field_no); + // ignore content not in line 21 + valid = valid && (11 == line_offset); + if (!valid) + continue; + cc608_hdr = 0xf8 | (valid ? 0x04 : 0x00) | type; + real_count++; + s->tmp_scte_cc_buf[s->tmp_scte_cc_len++] = cc608_hdr; + s->tmp_scte_cc_buf[s->tmp_scte_cc_len++] = cc_data_1; + s->tmp_scte_cc_buf[s->tmp_scte_cc_len++] = cc_data_2; + } + if (!real_count) + { + s->tmp_scte_cc_len = scte_cnt_loc; + } + else + { + s->tmp_scte_cc_buf[scte_cnt_loc] = 0x40 | (0x1f&real_count); + s->tmp_scte_cc_len = scte_cnt_loc + 2 + 3 * real_count; + } + } + } else if (buf_end - p >= 11 && + p[0] == 0x05 && p[1] == 0x02) { + /* parse EIA-608 captions embedded in a DVB stream. */ + Mpeg1Context *s1 = avctx->priv_data; + MpegEncContext *s = &s1->mpeg_enc_ctx; + uint8_t dvb_cc_type = p[7]; + p += 8; + + /* Predictive frame tag, but MythTV reorders predictive + * frames for us along with the CC data, so we ignore it. + */ + if (dvb_cc_type == 0x05) { + dvb_cc_type = p[6]; + p += 7; + } + + if (dvb_cc_type == 0x02) { /* 2-byte caption, can be repeated */ + int type = 0x00; // line 21 field 1 == 0x00, field 2 == 0x01 + uint8_t cc608_hdr = 0xf8 | 0x04/*valid*/ | type; + uint8_t hi = p[1] & 0xFF; + uint8_t lo = p[2] & 0xFF; + + dvb_cc_type = p[3]; + + if ((2 <= buf_end - p) && ((3 + s->tmp_atsc_cc_len) < ATSC_CC_BUF_SIZE)) { + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = 0x40 | (0x1f&1/*cc_count*/); + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = 0x00; // em_data + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = cc608_hdr; + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = hi; + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = lo; + + /* Only repeat characters when the next type flag + * is 0x04 and the characters are repeatable (i.e., less than + * 32 with the parity stripped). + */ + if (dvb_cc_type == 0x04 && (hi & 0x7f) < 32) { + if ((2 <= buf_end - p) && ((3 + s->tmp_atsc_cc_len) < ATSC_CC_BUF_SIZE)) { + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = 0x40 | (0x1f&1/*cc_count*/); + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = 0x00; // em_data + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = cc608_hdr; + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = hi; + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = lo; + } + } + } + + p += 6; + } else if (dvb_cc_type == 0x04) { /* 4-byte caption, not repeated */ + if ((4 <= buf_end - p) && + ((6 + s->tmp_atsc_cc_len) < ATSC_CC_BUF_SIZE)) { + int type = 0x00; // line 21 field 1 == 0x00, field 2 == 0x01 + uint8_t cc608_hdr = 0xf8 | 0x04/*valid*/ | type; + + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = 0x40 | (0x1f&2/*cc_count*/); + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = 0x00; // em_data + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = cc608_hdr; + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = p[1] & 0xFF; + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = p[2] & 0xFF; + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = cc608_hdr; + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = p[3] & 0xFF; + s->tmp_atsc_cc_buf[s->tmp_atsc_cc_len++] = p[4] & 0xFF; + } + + p += 9; + } + } else if (buf_end - p >= 6 && p[0] == 'J' && p[1] == 'P' && p[2] == '3' && p[3] == 'D' && p[4] == 0x03) { // S3D_video_format_length // the 0x7F mask ignores the reserved_bit value @@ -2304,6 +2489,9 @@ } else if (mpeg_decode_a53_cc(avctx, p, buf_size)) { return; } + // For other CEA-608 embedding options see: + /* SCTE 21 */ + /* ETSI EN 301 775 */ } static void mpeg_decode_gop(AVCodecContext *avctx, diff -ruN ./libavcodec/mpeg4videodec.c /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpeg4videodec.c --- ./libavcodec/mpeg4videodec.c 2014-08-09 07:27:13.874281245 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpeg4videodec.c 2014-08-09 07:18:01.610293201 +0200 @@ -118,6 +118,8 @@ if (s->workaround_bugs & FF_BUG_NO_PADDING && !ctx->resync_marker) return 0; +// disabling the while loop since it causes playback issues. see #3001 +#if 0 while (v <= 0xFF) { if (s->pict_type == AV_PICTURE_TYPE_B || (v >> (8 - s->pict_type) != 1) || s->partitioned_frame) @@ -126,6 +128,7 @@ bits_count += 8 + s->pict_type; v = show_bits(&s->gb, 16); } +#endif if (bits_count + 8 >= s->gb.size_in_bits) { v >>= 8; @@ -1770,7 +1773,9 @@ if (get_bits1(gb) != 0) /* fixed_vop_rate */ s->avctx->time_base.num = get_bits(gb, ctx->time_increment_bits); else - s->avctx->time_base.num = 1; + /* fix for h263 frame rate */ + s->avctx->time_base.num = (s->avctx->time_base.den>10000) ? 1001 : 1; + //s->avctx->time_base.num = 1; ctx->t_frame = 0; diff -ruN ./libavcodec/mpeg4videoenc.c /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpeg4videoenc.c --- ./libavcodec/mpeg4videoenc.c 2014-08-09 07:27:13.854281245 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpeg4videoenc.c 2014-08-09 07:18:01.610293201 +0200 @@ -1110,7 +1110,13 @@ time_div = FFUDIV(s->time, s->avctx->time_base.den); time_mod = FFUMOD(s->time, s->avctx->time_base.den); time_incr = time_div - s->last_time_base; - av_assert0(time_incr >= 0); + + if (time_incr < 0) + { + s->last_time_base = time_div; + time_incr = 0; + } + //MYTH assert0(time_incr >= 0); while (time_incr--) put_bits(&s->pb, 1, 1); diff -ruN ./libavcodec/mpegaudiodecheader.c /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpegaudiodecheader.c --- ./libavcodec/mpegaudiodecheader.c 2014-08-09 07:27:13.874281245 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpegaudiodecheader.c 2014-08-09 07:18:01.614293201 +0200 @@ -108,7 +108,7 @@ return 0; } -int avpriv_mpa_decode_header2(uint32_t head, int *sample_rate, int *channels, int *frame_size, int *bit_rate, enum AVCodecID *codec_id) +int avpriv_mpa_decode_header2(uint32_t head, int *sample_rate, int *channels, int *frame_size, int *bit_rate, enum AVCodecID *codec_id, int *dual_language) { MPADecodeHeader s1, *s = &s1; @@ -141,10 +141,13 @@ *sample_rate = s->sample_rate; *channels = s->nb_channels; *bit_rate = s->bit_rate; + if (dual_language) { + *dual_language = (s->mode == MPA_DUAL) ? 1 : 0; + } return s->frame_size; } int avpriv_mpa_decode_header(AVCodecContext *avctx, uint32_t head, int *sample_rate, int *channels, int *frame_size, int *bit_rate) { - return avpriv_mpa_decode_header2(head, sample_rate, channels, frame_size, bit_rate, &avctx->codec_id); + return avpriv_mpa_decode_header2(head, sample_rate, channels, frame_size, bit_rate, &avctx->codec_id, &avctx->avcodec_dual_language); } diff -ruN ./libavcodec/mpegaudiodecheader.h /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpegaudiodecheader.h --- ./libavcodec/mpegaudiodecheader.h 2014-08-09 07:27:13.854281245 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpegaudiodecheader.h 2014-08-09 07:18:01.614293201 +0200 @@ -56,7 +56,7 @@ header, otherwise the coded frame size in bytes */ int avpriv_mpa_decode_header(AVCodecContext *avctx, uint32_t head, int *sample_rate, int *channels, int *frame_size, int *bitrate); -int avpriv_mpa_decode_header2(uint32_t head, int *sample_rate, int *channels, int *frame_size, int *bitrate, enum AVCodecID *codec_id); +int avpriv_mpa_decode_header2(uint32_t head, int *sample_rate, int *channels, int *frame_size, int *bitrate, enum AVCodecID *codec_id, int *dual_langue); /* fast header check for resync */ static inline int ff_mpa_check_header(uint32_t header){ diff -ruN ./libavcodec/mpegaudiodec_template.c /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpegaudiodec_template.c --- ./libavcodec/mpegaudiodec_template.c 2014-08-09 07:27:13.890281244 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpegaudiodec_template.c 2014-08-09 07:18:01.614293201 +0200 @@ -1675,6 +1675,10 @@ av_log(avctx, AV_LOG_ERROR, "incomplete frame\n"); return AVERROR_INVALIDDATA; } else if (s->frame_size < buf_size) { + // FIXME, This 'if' statement can be taken out at some point in the + // future. For now, it prevents people's logs filling up with these + // errors when they play .nuv files created by the old mythtranscode + if ((buf_size % s->frame_size) != 0) av_log(avctx, AV_LOG_DEBUG, "incorrect frame size - multiple frames in buffer?\n"); buf_size= s->frame_size; } diff -ruN ./libavcodec/mpegaudio_parser.c /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpegaudio_parser.c --- ./libavcodec/mpegaudio_parser.c 2014-08-09 07:27:13.886281244 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpegaudio_parser.c 2014-08-09 07:18:01.610293201 +0200 @@ -68,7 +68,7 @@ state= (state<<8) + buf[i++]; - ret = avpriv_mpa_decode_header2(state, &sr, &channels, &frame_size, &bit_rate, &codec_id); + ret = avpriv_mpa_decode_header2(state, &sr, &channels, &frame_size, &bit_rate, &codec_id, &avctx->avcodec_dual_language); if (ret < 4) { if (i > 4) s->header_count = -2; @@ -79,7 +79,7 @@ s->header_count++; s->frame_size = ret-4; - if (s->header_count > 0 + (avctx->codec_id != AV_CODEC_ID_NONE && avctx->codec_id != codec_id)) { + if ((s->header_count > 0 + (avctx->codec_id != AV_CODEC_ID_NONE && avctx->codec_id != codec_id)) || avctx->sample_rate == 0) { avctx->sample_rate= sr; avctx->channels = channels; s1->duration = frame_size; diff -ruN ./libavcodec/mpegvideo.c /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpegvideo.c --- ./libavcodec/mpegvideo.c 2014-08-09 07:27:13.886281244 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpegvideo.c 2014-08-09 07:18:01.614293201 +0200 @@ -1701,6 +1701,16 @@ pic->reference = 3; } + /* Myth Change - Begin */ + /* Put ATSC captions cached from parse_user_data into the correct frame */ + memcpy(pic->f.atsc_cc_buf, s->tmp_atsc_cc_buf, s->tmp_atsc_cc_len); + pic->f.atsc_cc_len = s->tmp_atsc_cc_len; + s->tmp_atsc_cc_len = 0; + memcpy(pic->f.scte_cc_buf, s->tmp_scte_cc_buf, s->tmp_scte_cc_len); + pic->f.scte_cc_len = s->tmp_scte_cc_len; + s->tmp_scte_cc_len = 0; + /* Myth Change - End */ + pic->f.coded_picture_number = s->coded_picture_number++; if (ff_alloc_picture(s, pic, 0) < 0) diff -ruN ./libavcodec/mpegvideo.h /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpegvideo.h --- ./libavcodec/mpegvideo.h 2014-08-09 07:27:13.854281245 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/mpegvideo.h 2014-08-09 07:18:01.618293201 +0200 @@ -272,7 +272,7 @@ * MpegEncContext. */ typedef struct MpegEncContext { - AVClass *class; + AVClass *clss; struct AVCodecContext *avctx; /* the following parameters must be initialized before encoding */ int width, height;///< picture size. must be a multiple of 16 @@ -693,6 +693,15 @@ char *tc_opt_str; ///< timecode option string AVTimecode tc; ///< timecode context +#define ATSC_CC_BUF_SIZE 1024 + /// Used to hold cached user_data about caption packets before the + /// frame for these packets has been created in MPV_frame_start(). + uint8_t tmp_atsc_cc_buf[ATSC_CC_BUF_SIZE]; + int tmp_atsc_cc_len; +#define SCTE_CC_BUF_SIZE 1024 + uint8_t tmp_scte_cc_buf[SCTE_CC_BUF_SIZE]; + int tmp_scte_cc_len; + uint8_t *ptr_lastgob; int swap_uv; //vcr2 codec is an MPEG-2 variant with U and V swapped int pack_pblocks; //xvmc needs to keep blocks without gaps. diff -ruN ./libavcodec/pcm.c /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/pcm.c --- ./libavcodec/pcm.c 2014-08-09 07:27:13.882281244 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/pcm.c 2014-08-09 07:18:01.626293200 +0200 @@ -228,7 +228,10 @@ PCMDecode *s = avctx->priv_data; int i; - if (avctx->channels <= 0) { +// Breaks playback of some DVDs which feature an empty audio stream +// +// if (avctx->channels <= 0) { + if (avctx->channels < 0) { av_log(avctx, AV_LOG_ERROR, "PCM channels out of bounds\n"); return AVERROR(EINVAL); } diff -ruN ./libavcodec/pgssubdec.c /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/pgssubdec.c --- ./libavcodec/pgssubdec.c 2014-08-09 07:27:13.890281244 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/pgssubdec.c 2014-08-09 07:18:01.626293200 +0200 @@ -53,6 +53,7 @@ typedef struct PGSSubPresentation { int id_number; int object_count; + int object_forced; PGSSubPictureReference *objects; int64_t pts; } PGSSubPresentation; @@ -355,6 +356,7 @@ buf++; /* composition_flag (0x80 - object cropped, 0x40 - object forced) */ reference->composition = bytestream_get_byte(&buf); + ctx->presentation.object_forced = (reference->composition & 0x40) >> 6; reference->x = bytestream_get_be16(&buf); reference->y = bytestream_get_be16(&buf); @@ -412,6 +414,7 @@ if (!ctx->presentation.object_count) return 1; + sub->forced = ctx->presentation.object_forced; sub->start_display_time = 0; sub->end_display_time = 20000; sub->format = 0; diff -ruN ./libavcodec/utils-mythtv.c /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/utils-mythtv.c --- ./libavcodec/utils-mythtv.c 1970-01-01 01:00:00.000000000 +0100 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavcodec/utils-mythtv.c 2014-08-09 07:18:01.666293200 +0200 @@ -0,0 +1,243 @@ +/* + * utils for libavcodec + * Copyright (c) 2001 Fabrice Bellard. + * Copyright (c) 2003 Michel Bardiaux for the av_log API + * Copyright (c) 2002-2004 Michael Niedermayer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file myth_utils.c + * myth_utils. + */ + +#include "avcodec.h" + +/** \fn codec_id_string(enum AVCodecID) + * returns a human readable string for the AVCodecID enum. + */ +const char *ff_codec_id_string(enum AVCodecID codec_id) +{ + switch (codec_id) + { + case AV_CODEC_ID_NONE: return "NONE"; + case AV_CODEC_ID_MPEG1VIDEO: return "MPEG1VIDEO"; + case AV_CODEC_ID_MPEG2VIDEO: return "MPEG2VIDEO"; + case AV_CODEC_ID_MPEG2VIDEO_XVMC: return "MPEG2VIDEO_XVMC"; + case AV_CODEC_ID_H261: return "H261"; + case AV_CODEC_ID_H263: return "H263"; + case AV_CODEC_ID_RV10: return "RV10"; + case AV_CODEC_ID_RV20: return "RV20"; + case AV_CODEC_ID_MJPEG: return "MJPEG"; + case AV_CODEC_ID_MJPEGB: return "MJPEGB"; + case AV_CODEC_ID_LJPEG: return "LJPEG"; + case AV_CODEC_ID_SP5X: return "SP5X"; + case AV_CODEC_ID_MPEG4: return "MPEG4"; + case AV_CODEC_ID_RAWVIDEO: return "RAWVIDEO"; + case AV_CODEC_ID_MSMPEG4V1: return "MSMPEG4V1"; + case AV_CODEC_ID_MSMPEG4V2: return "MSMPEG4V2"; + case AV_CODEC_ID_MSMPEG4V3: return "MSMPEG4V3"; + case AV_CODEC_ID_WMV1: return "WMV1"; + case AV_CODEC_ID_WMV2: return "WMV2"; + case AV_CODEC_ID_H263P: return "H263P"; + case AV_CODEC_ID_H263I: return "H263I"; + case AV_CODEC_ID_FLV1: return "FLV1"; + case AV_CODEC_ID_SVQ1: return "SVQ1"; + case AV_CODEC_ID_SVQ3: return "SVQ3"; + case AV_CODEC_ID_DVVIDEO: return "DVVIDEO"; + case AV_CODEC_ID_HUFFYUV: return "HUFFYUV"; + case AV_CODEC_ID_CYUV: return "CYUV"; + case AV_CODEC_ID_H264: return "H264"; + case AV_CODEC_ID_INDEO3: return "INDEO3"; + case AV_CODEC_ID_VP3: return "VP3"; + case AV_CODEC_ID_THEORA: return "THEORA"; + case AV_CODEC_ID_ASV1: return "ASV1"; + case AV_CODEC_ID_ASV2: return "ASV2"; + case AV_CODEC_ID_FFV1: return "FFV1"; + case AV_CODEC_ID_4XM: return "4XM"; + case AV_CODEC_ID_VCR1: return "VCR1"; + case AV_CODEC_ID_CLJR: return "CLJR"; + case AV_CODEC_ID_MDEC: return "MDEC"; + case AV_CODEC_ID_ROQ: return "ROQ"; + case AV_CODEC_ID_INTERPLAY_VIDEO: return "INTERPLAY_VIDEO"; + case AV_CODEC_ID_XAN_WC3: return "XAN_WC3"; + case AV_CODEC_ID_XAN_WC4: return "XAN_WC4"; + case AV_CODEC_ID_RPZA: return "RPZA"; + case AV_CODEC_ID_CINEPAK: return "CINEPAK"; + case AV_CODEC_ID_WS_VQA: return "WS_VQA"; + case AV_CODEC_ID_MSRLE: return "MSRLE"; + case AV_CODEC_ID_MSVIDEO1: return "MSVIDEO1"; + case AV_CODEC_ID_IDCIN: return "IDCIN"; + case AV_CODEC_ID_8BPS: return "8BPS"; + case AV_CODEC_ID_SMC: return "SMC"; + case AV_CODEC_ID_FLIC: return "FLIC"; + case AV_CODEC_ID_TRUEMOTION1: return "TRUEMOTION1"; + case AV_CODEC_ID_VMDVIDEO: return "VMDVIDEO"; + case AV_CODEC_ID_MSZH: return "MSZH"; + case AV_CODEC_ID_ZLIB: return "ZLIB"; + case AV_CODEC_ID_QTRLE: return "QTRLE"; + case AV_CODEC_ID_SNOW: return "SNOW"; + case AV_CODEC_ID_TSCC: return "TSCC"; + case AV_CODEC_ID_ULTI: return "ULTI"; + case AV_CODEC_ID_QDRAW: return "QDRAW"; + case AV_CODEC_ID_VIXL: return "VIXL"; + case AV_CODEC_ID_QPEG: return "QPEG"; +#if 0 + case AV_CODEC_ID_XVID: return "XVID"; +#endif + case AV_CODEC_ID_PNG: return "PNG"; + case AV_CODEC_ID_PPM: return "PPM"; + case AV_CODEC_ID_PBM: return "PBM"; + case AV_CODEC_ID_PGM: return "PGM"; + case AV_CODEC_ID_PGMYUV: return "PGMYUV"; + case AV_CODEC_ID_PAM: return "PAM"; + case AV_CODEC_ID_FFVHUFF: return "FFVHUFF"; + case AV_CODEC_ID_RV30: return "RV30"; + case AV_CODEC_ID_RV40: return "RV40"; + case AV_CODEC_ID_VC1: return "VC1"; + case AV_CODEC_ID_WMV3: return "WMV3"; + case AV_CODEC_ID_LOCO: return "LOCO"; + case AV_CODEC_ID_WNV1: return "WNV1"; + case AV_CODEC_ID_AASC: return "AASC"; + case AV_CODEC_ID_INDEO2: return "INDEO2"; + case AV_CODEC_ID_FRAPS: return "FRAPS"; + case AV_CODEC_ID_TRUEMOTION2: return "TRUEMOTION2"; + case AV_CODEC_ID_BMP: return "BMP"; + case AV_CODEC_ID_VP8: return "VP8"; + + /* various pcm "codecs" */ + case AV_CODEC_ID_PCM_S16LE: return "PCM_S16LE"; + case AV_CODEC_ID_PCM_S16BE: return "PCM_S16BE"; + case AV_CODEC_ID_PCM_U16LE: return "PCM_U16LE"; + case AV_CODEC_ID_PCM_U16BE: return "PCM_U16BE"; + case AV_CODEC_ID_PCM_S8: return "PCM_S8"; + case AV_CODEC_ID_PCM_U8: return "PCM_U8"; + case AV_CODEC_ID_PCM_MULAW: return "PCM_MULAW"; + case AV_CODEC_ID_PCM_ALAW: return "PCM_ALAW"; + case AV_CODEC_ID_PCM_S32LE: return "PCM_S32LE"; + case AV_CODEC_ID_PCM_S32BE: return "PCM_S32BE"; + case AV_CODEC_ID_PCM_U32LE: return "PCM_U32LE"; + case AV_CODEC_ID_PCM_U32BE: return "PCM_U32BE"; + case AV_CODEC_ID_PCM_S24LE: return "PCM_S24LE"; + case AV_CODEC_ID_PCM_S24BE: return "PCM_S24BE"; + case AV_CODEC_ID_PCM_U24LE: return "PCM_U24LE"; + case AV_CODEC_ID_PCM_U24BE: return "PCM_U24BE"; + case AV_CODEC_ID_PCM_S24DAUD: return "PCM_S24DAUD"; + case AV_CODEC_ID_PCM_DVD: return "PCM_DVD"; + case AV_CODEC_ID_PCM_BLURAY: return "PCM_BLURAY"; + + /* various adpcm codecs */ + case AV_CODEC_ID_ADPCM_IMA_QT: return "ADPCM_IMA_QT"; + case AV_CODEC_ID_ADPCM_IMA_WAV: return "ADPCM_IMA_WAV"; + case AV_CODEC_ID_ADPCM_IMA_DK3: return "ADPCM_IMA_DK3"; + case AV_CODEC_ID_ADPCM_IMA_DK4: return "ADPCM_IMA_DK4"; + case AV_CODEC_ID_ADPCM_IMA_WS: return "ADPCM_IMA_WS"; + case AV_CODEC_ID_ADPCM_IMA_SMJPEG: return "ADPCM_IMA_SMJPEG"; + case AV_CODEC_ID_ADPCM_MS: return "ADPCM_MS"; + case AV_CODEC_ID_ADPCM_4XM: return "ADPCM_4XM"; + case AV_CODEC_ID_ADPCM_XA: return "ADPCM_XA"; + case AV_CODEC_ID_ADPCM_ADX: return "ADPCM_ADX"; + case AV_CODEC_ID_ADPCM_EA: return "ADPCM_EA"; + case AV_CODEC_ID_ADPCM_G726: return "ADPCM_G726"; + case AV_CODEC_ID_ADPCM_CT: return "ADPCM_CT"; + case AV_CODEC_ID_ADPCM_SWF: return "ADPCM_SWF"; + case AV_CODEC_ID_ADPCM_YAMAHA: return "ADPCM_YAMAHA"; + + /* AMR */ + case AV_CODEC_ID_AMR_NB: return "AMR_NB"; + case AV_CODEC_ID_AMR_WB: return "AMR_WB"; + + /* RealAudio codecs*/ + case AV_CODEC_ID_RA_144: return "RA_144"; + case AV_CODEC_ID_RA_288: return "RA_288"; + + /* various DPCM codecs */ + case AV_CODEC_ID_ROQ_DPCM: return "ROQ_DPCM"; + case AV_CODEC_ID_INTERPLAY_DPCM: return "INTERPLAY_DPCM"; + case AV_CODEC_ID_XAN_DPCM: return "XAN_DPCM"; + case AV_CODEC_ID_SOL_DPCM: return "SOL_DPCM"; + + case AV_CODEC_ID_MP2: return "MP2"; + case AV_CODEC_ID_MP3: return "MP3"; + case AV_CODEC_ID_AAC: return "AAC"; + case AV_CODEC_ID_AAC_LATM: return "AAC/LATM"; + case AV_CODEC_ID_AC3: return "AC3"; + case AV_CODEC_ID_DTS: return "DTS"; + case AV_CODEC_ID_VORBIS: return "VORBIS"; + case AV_CODEC_ID_DVAUDIO: return "DVAUDIO"; + case AV_CODEC_ID_WMAV1: return "WMAV1"; + case AV_CODEC_ID_WMAV2: return "WMAV2"; + case AV_CODEC_ID_MACE3: return "MACE3"; + case AV_CODEC_ID_MACE6: return "MACE6"; + case AV_CODEC_ID_VMDAUDIO: return "VMDAUDIO"; + case AV_CODEC_ID_SONIC: return "SONIC"; + case AV_CODEC_ID_SONIC_LS: return "SONIC_LS"; + case AV_CODEC_ID_FLAC: return "FLAC"; + case AV_CODEC_ID_MP3ADU: return "MP3ADU"; + case AV_CODEC_ID_MP3ON4: return "MP3ON4"; + case AV_CODEC_ID_SHORTEN: return "SHORTEN"; + case AV_CODEC_ID_ALAC: return "ALAC"; + case AV_CODEC_ID_WESTWOOD_SND1: return "WESTWOOD_SND1"; + case AV_CODEC_ID_GSM: return "GSM"; + case AV_CODEC_ID_QDM2: return "QDM2"; + case AV_CODEC_ID_MLP: return "MLP"; + case AV_CODEC_ID_TRUEHD: return "TRUEHD"; + case AV_CODEC_ID_EAC3: return "E-AC3"; + case AV_CODEC_ID_WMAPRO: return "WMAPRO"; + + /* subtitle codecs */ + case AV_CODEC_ID_DVD_SUBTITLE: return "DVD_SUBTITLE"; + case AV_CODEC_ID_DVB_SUBTITLE: return "DVB_SUBTITLE"; + case AV_CODEC_ID_TEXT: return "TEXT SUBTITLE"; + case AV_CODEC_ID_XSUB: return "XSUB SUBTITLE"; + case AV_CODEC_ID_SSA: return "SSA SUBTITLE"; + case AV_CODEC_ID_HDMV_PGS_SUBTITLE: return "HDMV_PGS_SUBTITLE"; + case AV_CODEC_ID_DVB_TELETEXT: return "DVB_TELETEXT"; + + case AV_CODEC_ID_MPEG2VBI: return "MPEG2VBI"; + case AV_CODEC_ID_DVB_VBI: return "DVB_VBI"; + + case AV_CODEC_ID_DSMCC_B: return "DSMCC_B"; + + case AV_CODEC_ID_DVD_NAV: return "DVD NAV"; + + case AV_CODEC_ID_MPEG2TS: return "MPEG2TS"; + + /* Attachment codecs */ + case AV_CODEC_ID_TTF: return "TTF font"; + + case AV_CODEC_ID_PROBE: return "PROBE"; + } + return "Unknown Codec ID"; +} + +/** \fn codec_type_string(enum CodecType) + * returns a human readable string for the CodecType enum. + */ +const char *ff_codec_type_string(enum AVMediaType codec_type) +{ + switch (codec_type) + { + case AVMEDIA_TYPE_UNKNOWN: return "Unknown"; + case AVMEDIA_TYPE_VIDEO: return "Video"; + case AVMEDIA_TYPE_AUDIO: return "Audio"; + case AVMEDIA_TYPE_DATA: return "Data"; + case AVMEDIA_TYPE_SUBTITLE: return "Subtitle"; + case AVMEDIA_TYPE_ATTACHMENT: return "Attachment"; + } + return "Invalid Codec Type"; +}; + diff -ruN ./libavformat/allformats.c /tmp/mythtv/mythtv/external/FFmpeg/libavformat/allformats.c --- ./libavformat/allformats.c 2014-08-09 07:27:10.274281323 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavformat/allformats.c 2014-08-09 07:18:01.742293198 +0200 @@ -184,6 +184,8 @@ REGISTER_MUXER (MPEG2VIDEO, mpeg2video); REGISTER_MUXER (MPEG2VOB, mpeg2vob); REGISTER_DEMUXER (MPEGPS, mpegps); + REGISTER_DEMUXER (MPEGTS, mythtv_mpegts); + REGISTER_DEMUXER (MPEGTSRAW, mythtv_mpegtsraw); REGISTER_MUXDEMUX(MPEGTS, mpegts); REGISTER_DEMUXER (MPEGTSRAW, mpegtsraw); REGISTER_DEMUXER (MPEGVIDEO, mpegvideo); diff -ruN ./libavformat/avformat.h /tmp/mythtv/mythtv/external/FFmpeg/libavformat/avformat.h --- ./libavformat/avformat.h 2014-08-09 07:27:10.278281322 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavformat/avformat.h 2014-08-09 07:18:01.746293198 +0200 @@ -912,6 +912,12 @@ int nb_index_entries; unsigned int index_entries_allocated_size; + /* mythtv addons */ + int got_frame; + + int component_tag; ///< Component tag given in PMT, for MythTV MHEG + /* end mythtv addons */ + /** * Real base framerate of the stream. * This is the lowest framerate with which all timestamps can be @@ -1181,6 +1187,16 @@ unsigned int packet_size; int max_delay; + /* Myth addons */ + int build_index; + + /* mpeg-ts support */ + void (*streams_changed)(void*); + void *stream_change_data; + const uint8_t *cur_pmt_sect; + int cur_pmt_sect_len; + /* End Myth addons */ + int flags; #define AVFMT_FLAG_GENPTS 0x0001 ///< Generate missing pts even if it requires parsing future frames. #define AVFMT_FLAG_IGNIDX 0x0002 ///< Ignore index. @@ -2527,6 +2543,17 @@ int avformat_queue_attached_pictures(AVFormatContext *s); +/* MythTV changes */ + +#define MAX_STREAMS 100 + +void estimate_timings(AVFormatContext *ic, int64_t old_offset); +void av_estimate_timings(AVFormatContext *ic, int64_t old_offset); +AVStream *av_add_stream(AVFormatContext *s, AVStream *st, int id); +void av_remove_stream(AVFormatContext *s, int id, int remove_ts); +void flush_packet_queue(AVFormatContext *s); +/* End MythTV changes */ + /** * @} */ diff -ruN ./libavformat/libavformat.v /tmp/mythtv/mythtv/external/FFmpeg/libavformat/libavformat.v --- ./libavformat/libavformat.v 2014-08-09 07:27:10.274281323 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavformat/libavformat.v 2014-08-09 07:18:01.762293197 +0200 @@ -16,6 +16,7 @@ ffio_set_buf_size; ffurl_close; ffurl_open; + ffurl_read; ffurl_read_complete; ffurl_seek; ffurl_size; @@ -33,5 +34,6 @@ get_*; put_*; ff_codec_get_id; + ff_read_frame_flush; local: *; }; diff -ruN ./libavformat/Makefile /tmp/mythtv/mythtv/external/FFmpeg/libavformat/Makefile --- ./libavformat/Makefile 2014-08-09 07:27:10.274281323 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavformat/Makefile 2014-08-09 07:18:01.742293198 +0200 @@ -23,6 +23,7 @@ seek.o \ url.o \ utils.o \ + utils-mythtv.o \ OBJS-$(CONFIG_NETWORK) += network.o OBJS-$(CONFIG_RIFFDEC) += riffdec.o @@ -232,6 +233,7 @@ OBJS-$(CONFIG_MPEG1VIDEO_MUXER) += rawenc.o OBJS-$(CONFIG_MPEG2VIDEO_MUXER) += rawenc.o OBJS-$(CONFIG_MPEGPS_DEMUXER) += mpeg.o +OBJS-$(CONFIG_MPEGTS_DEMUXER) += mpegts-mythtv.o isom.o OBJS-$(CONFIG_MPEGTS_DEMUXER) += mpegts.o isom.o OBJS-$(CONFIG_MPEGTS_MUXER) += mpegtsenc.o OBJS-$(CONFIG_MPEGVIDEO_DEMUXER) += mpegvideodec.o rawdec.o diff -ruN ./libavformat/mov.c /tmp/mythtv/mythtv/external/FFmpeg/libavformat/mov.c --- ./libavformat/mov.c 2014-08-09 07:27:10.274281323 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavformat/mov.c 2014-08-09 07:18:01.766293197 +0200 @@ -3459,8 +3459,8 @@ if (!sample || (!s->pb->seekable && current_sample->pos < sample->pos) || (s->pb->seekable && ((msc->pb != s->pb && dts < best_dts) || (msc->pb == s->pb && - ((FFABS(best_dts - dts) <= AV_TIME_BASE && current_sample->pos < sample->pos) || - (FFABS(best_dts - dts) > AV_TIME_BASE && dts < best_dts)))))) { + ((FFABS(best_dts - dts) <= 4*AV_TIME_BASE && current_sample->pos < sample->pos) || + (FFABS(best_dts - dts) > 4*AV_TIME_BASE && dts < best_dts)))))) { sample = current_sample; best_dts = dts; *st = avst; diff -ruN ./libavformat/mpeg.c /tmp/mythtv/mythtv/external/FFmpeg/libavformat/mpeg.c --- ./libavformat/mpeg.c 2014-08-09 07:27:10.270281323 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavformat/mpeg.c 2014-08-09 07:18:01.770293197 +0200 @@ -496,6 +496,9 @@ } else if(es_type == STREAM_TYPE_AUDIO_AAC){ codec_id = AV_CODEC_ID_AAC; type = AVMEDIA_TYPE_AUDIO; + } else if(es_type == STREAM_TYPE_AUDIO_AAC_LATM){ + codec_id = AV_CODEC_ID_AAC_LATM; + type = AVMEDIA_TYPE_AUDIO; } else if(es_type == STREAM_TYPE_VIDEO_MPEG4){ codec_id = AV_CODEC_ID_MPEG4; type = AVMEDIA_TYPE_VIDEO; @@ -555,6 +558,9 @@ } else if (startcode >= 0x20 && startcode <= 0x3f) { type = AVMEDIA_TYPE_SUBTITLE; codec_id = AV_CODEC_ID_DVD_SUBTITLE; + } else if (startcode == 0x69 || startcode == 0x49) { + type = AVMEDIA_TYPE_DATA; + codec_id = AV_CODEC_ID_MPEG2VBI; } else if (startcode >= 0xfd55 && startcode <= 0xfd5f) { type = AVMEDIA_TYPE_VIDEO; codec_id = AV_CODEC_ID_VC1; @@ -578,6 +584,12 @@ } st->request_probe = request_probe; st->need_parsing = AVSTREAM_PARSE_FULL; + + /* notify the callback of the change in streams */ + if (s->streams_changed) { + s->streams_changed(s->stream_change_data); + } + found: if(st->discard >= AVDISCARD_ALL) goto skip; diff -ruN ./libavformat/mpeg.h /tmp/mythtv/mythtv/external/FFmpeg/libavformat/mpeg.h --- ./libavformat/mpeg.h 2014-08-09 07:27:10.270281323 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavformat/mpeg.h 2014-08-09 07:18:01.770293197 +0200 @@ -53,6 +53,7 @@ #define STREAM_TYPE_PRIVATE_DATA 0x06 #define STREAM_TYPE_AUDIO_AAC 0x0f #define STREAM_TYPE_VIDEO_MPEG4 0x10 +#define STREAM_TYPE_AUDIO_AAC_LATM 0x11 #define STREAM_TYPE_VIDEO_H264 0x1b #define STREAM_TYPE_VIDEO_CAVS 0x42 diff -ruN ./libavformat/mpegts.c /tmp/mythtv/mythtv/external/FFmpeg/libavformat/mpegts.c --- ./libavformat/mpegts.c 2014-08-09 07:27:10.274281323 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavformat/mpegts.c 2014-08-09 07:18:01.770293197 +0200 @@ -1406,7 +1406,7 @@ av_free(mp4_descr[i].dec_config_descr); } -int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type, +int ff_old_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type, const uint8_t **pp, const uint8_t *desc_list_end, Mp4Descr *mp4_descr, int mp4_descr_count, int pid, MpegTSContext *ts) @@ -1754,7 +1754,7 @@ if (desc_list_end > p_end) break; for(;;) { - if (ff_parse_mpeg2_descriptor(ts->stream, st, stream_type, &p, desc_list_end, + if (ff_old_parse_mpeg2_descriptor(ts->stream, st, stream_type, &p, desc_list_end, mp4_descr, mp4_descr_count, pid, ts) < 0) break; @@ -2518,7 +2518,7 @@ /**************************************************************/ /* parsing functions - called from other demuxers such as RTP */ -MpegTSContext *ff_mpegts_parse_open(AVFormatContext *s) +MpegTSContext *ff_old_mpegts_parse_open(AVFormatContext *s) { MpegTSContext *ts; @@ -2537,7 +2537,7 @@ /* return the consumed length if a packet was output, or -1 if no packet is output */ -int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, +int ff_old_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, const uint8_t *buf, int len) { int len1; @@ -2562,14 +2562,14 @@ return len1 - len; } -void ff_mpegts_parse_close(MpegTSContext *ts) +void ff_old_mpegts_parse_close(MpegTSContext *ts) { mpegts_free(ts); av_free(ts); } AVInputFormat ff_mpegts_demuxer = { - .name = "mpegts", + .name = "mpegts-ffmpeg", .long_name = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"), .priv_data_size = sizeof(MpegTSContext), .read_probe = mpegts_probe, @@ -2582,7 +2582,7 @@ }; AVInputFormat ff_mpegtsraw_demuxer = { - .name = "mpegtsraw", + .name = "mpegtsraw-ffmpeg", .long_name = NULL_IF_CONFIG_SMALL("raw MPEG-TS (MPEG-2 Transport Stream)"), .priv_data_size = sizeof(MpegTSContext), .read_header = mpegts_read_header, diff -ruN ./libavformat/mpegts.h /tmp/mythtv/mythtv/external/FFmpeg/libavformat/mpegts.h --- ./libavformat/mpegts.h 2014-08-09 07:27:10.270281323 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavformat/mpegts.h 2014-08-09 07:18:01.770293197 +0200 @@ -61,10 +61,10 @@ typedef struct MpegTSContext MpegTSContext; -MpegTSContext *ff_mpegts_parse_open(AVFormatContext *s); -int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, +MpegTSContext *ff_old_mpegts_parse_open(AVFormatContext *s); +int ff_old_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, const uint8_t *buf, int len); -void ff_mpegts_parse_close(MpegTSContext *ts); +void ff_old_mpegts_parse_close(MpegTSContext *ts); typedef struct SLConfigDescr { int use_au_start; @@ -99,7 +99,7 @@ * @param desc_list_end End of buffer * @return <0 to stop processing */ -int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type, +int ff_old_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type, const uint8_t **pp, const uint8_t *desc_list_end, Mp4Descr *mp4_descr, int mp4_descr_count, int pid, MpegTSContext *ts); diff -ruN ./libavformat/mpegts-mythtv.c /tmp/mythtv/mythtv/external/FFmpeg/libavformat/mpegts-mythtv.c --- ./libavformat/mpegts-mythtv.c 1970-01-01 01:00:00.000000000 +0100 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavformat/mpegts-mythtv.c 2014-08-09 07:18:01.770293197 +0200 @@ -0,0 +1,3197 @@ +/* + * MPEG2 transport stream (aka DVB) demuxer + * Copyright (c) 2002-2003 Fabrice Bellard + * Reworked for use with MythTV + * + * This file is part of MythTV. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/crc.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/log.h" +#include "libavutil/dict.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "libavutil/avassert.h" +#include "libavcodec/bytestream.h" +#include "libavcodec/avcodec.h" +#include "libavcodec/get_bits.h" +#include "avformat.h" +#include "mpegts-mythtv.h" +#include "internal.h" +#include "avio_internal.h" +#include "seek.h" +#include "mpeg.h" +#include "isom.h" + +/* maximum size in which we look for synchronisation if + synchronisation is lost */ +#define MAX_RESYNC_SIZE 65536 + +#define MAX_PES_PAYLOAD 200*1024 + +#define MAX_MP4_DESCR_COUNT 16 + +#define PMT_NOT_YET_FOUND 0 +#define PMT_NOT_IN_PAT 1 +#define PMT_FOUND 2 + +typedef struct SectionContext SectionContext; + +static SectionContext *add_section_stream(MpegTSContext *ts, int pid, int stream_type); +static void mpegts_cleanup_streams(MpegTSContext *ts); +static int find_in_list(const int *pids, int pid); + +enum MpegTSFilterType { + MPEGTS_PES, + MPEGTS_SECTION, +}; + +typedef struct MpegTSFilter MpegTSFilter; + + +static int is_pat_same(MpegTSContext *mpegts_ctx, + int *pmt_pnums, int *pmts_pids, unsigned int pmt_count); + +static void mpegts_add_stream(MpegTSContext *ts, int id, pmt_entry_t* item, uint32_t prog_reg_desc, int pcr_pid); +static int is_pmt_same(MpegTSContext *mpegts_ctx, pmt_entry_t* items, + int item_cnt); + +typedef int PESCallback(MpegTSFilter *f, const uint8_t *buf, int len, int is_start, int64_t pos); + +typedef struct MpegTSPESFilter { + PESCallback *pes_cb; + void *opaque; +} MpegTSPESFilter; + +typedef void SectionCallback(MpegTSFilter *f, const uint8_t *buf, int len); + +typedef void SetServiceCallback(void *opaque, int ret); + +typedef struct MpegTSSectionFilter { + int section_index; + int section_h_size; + uint8_t *section_buf; + unsigned int check_crc:1; + unsigned int end_of_section_reached:1; + SectionCallback *section_cb; + void *opaque; +} MpegTSSectionFilter; + +struct MpegTSFilter { + int pid; + int es_id; + int last_cc; /* last cc code (-1 if first packet) */ + enum MpegTSFilterType type; + /** if set, chop off PMT at the end of the TS packet, regardless of the + * data length given in the packet. This is for use by BBC iPlayer IPTV + * recordings which seem to only want to send the first packet of the PMT + * but give a length that requires 3 packets. Without this, those + * recordings are unplayable */ + int pmt_chop_at_ts; + union { + MpegTSPESFilter pes_filter; + MpegTSSectionFilter section_filter; + } u; +}; + +/** maximum number of PMT's we expect to be described in a PAT */ +#define PAT_MAX_PMT 128 + +/** maximum number of streams we expect to be described in a PMT */ +#define PMT_PIDS_MAX 256 + +#define MAX_PIDS_PER_PROGRAM 64 +struct Program { + unsigned int id; //program id/service id + unsigned int pid; // PMT PID + unsigned int nb_pids; + unsigned int pids[MAX_PIDS_PER_PROGRAM]; +}; + +struct MpegTSContext { + const AVClass *class; + /* user data */ + AVFormatContext *stream; + /** raw packet size, including FEC if present */ + int raw_packet_size; + + int pos47; + + /** if true, all pids are analyzed to find streams */ + int auto_guess; + + /** compute exact PCR for each transport stream packet */ + int mpeg2ts_compute_pcr; + + int64_t cur_pcr; /**< used to estimate the exact PCR */ + int pcr_incr; /**< used to estimate the exact PCR */ + + /** if set, stop_parse is set when PAT/PMT is found */ + int scanning; + /* data needed to handle file based ts */ + /** stop parsing loop */ + int stop_parse; + /** set to PMT_NOT_IN_PAT in pat_cb when scanning is true + * and the MPEG program number "req_sid" is not found in PAT; + * set to PMT_FOUND when a PMT with a the "req_sid" program + * number is found. */ + int pmt_scan_state; + /** packet containing Audio/Video data */ + AVPacket *pkt; + /** to detect seek */ + int64_t last_pos; + + /******************************************/ + /* private mpegts data */ + /* scan context */ + /** structure to keep track of Program->pids mapping */ + unsigned int nb_prg; + struct Program *prg; + + /** filter for the PAT */ + MpegTSFilter *pat_filter; + /** filter for the PMT for the MPEG program number specified by req_sid */ + MpegTSFilter *pmt_filter; + /** MPEG program number of stream we want to decode */ + int req_sid; + + /** filters for various streams specified by PMT + for the PAT and PMT */ + MpegTSFilter *pids[NB_PID_MAX]; + + /** number of streams in the last PMT seen */ + int pid_cnt; + /** list of streams in the last PMT seen */ + int pmt_pids[PMT_PIDS_MAX]; +}; + +static const AVOption options[] = { + {"compute_pcr", "Compute exact PCR for each transport stream packet.", offsetof(MpegTSContext, mpeg2ts_compute_pcr), AV_OPT_TYPE_INT, + {.dbl = 0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass mpegtsraw_class = { + .class_name = "mpegtsraw demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +/* TS stream handling */ + +enum MpegTSState { + MPEGTS_HEADER = 0, + MPEGTS_PESHEADER, + MPEGTS_PESHEADER_FILL, + MPEGTS_PAYLOAD, + MPEGTS_SKIP, +}; + +/* enough for PES header + length */ +#define PES_START_SIZE 6 +#define PES_HEADER_SIZE 9 +#define MAX_PES_HEADER_SIZE (9 + 255) + +typedef struct PESContext { + int pid; + int pcr_pid; /**< if -1 then all packets containing PCR are considered */ + int stream_type; + MpegTSContext *ts; + AVFormatContext *stream; + AVStream *st; + AVStream *sub_st; /**< stream for the embedded AC3 stream in HDMV TrueHD */ + enum MpegTSState state; + /* used to get the format */ + int data_index; + int flags; /**< copied to the AVPacket flags */ + int total_size; + int pes_header_size; + int extended_stream_id; + int64_t pts, dts; + int64_t ts_packet_pos; /**< position of first TS packet of this PES packet */ + uint8_t header[MAX_PES_HEADER_SIZE]; + uint8_t *buffer; + SLConfigDescr sl; +} PESContext; + +extern AVInputFormat ff_mythtv_mpegts_demuxer; + +struct SectionContext { + int pid; + int stream_type; + int new_packet; + MpegTSContext *ts; + AVFormatContext *stream; + AVStream *st; +}; + +static void clear_program(MpegTSContext *ts, unsigned int programid) +{ + int i; + + for(i=0; inb_prg; i++) + if(ts->prg[i].id == programid) + ts->prg[i].nb_pids = 0; +} + +static void clear_programs(MpegTSContext *ts) +{ + av_freep(&ts->prg); + ts->nb_prg=0; +} + +static void add_pat_entry(MpegTSContext *ts, unsigned int programid, unsigned int pid) +{ + struct Program *p; + void *tmp = av_realloc(ts->prg, (ts->nb_prg+1)*sizeof(struct Program)); + if(!tmp) + return; + ts->prg = tmp; + p = &ts->prg[ts->nb_prg]; + p->id = programid; + p->pid = pid; + p->nb_pids = 0; + ts->nb_prg++; +} + +static void add_pid_to_pmt(MpegTSContext *ts, unsigned int programid, unsigned int pid) +{ + int i; + struct Program *p = NULL; + for(i=0; inb_prg; i++) { + if(ts->prg[i].id == programid) { + p = &ts->prg[i]; + break; + } + } + if(!p) + return; + + if(p->nb_pids >= MAX_PIDS_PER_PROGRAM) + return; + p->pids[p->nb_pids++] = pid; +} + +static void set_pcr_pid(AVFormatContext *s, unsigned int programid, unsigned int pid) +{ + int i; + for(i=0; inb_programs; i++) { + if(s->programs[i]->id == programid) { + s->programs[i]->pcr_pid = pid; + break; + } + } +} + +static void mpegts_close_filter(MpegTSContext *ts, MpegTSFilter *filter); + +/** + * @brief discard_pid() decides if the pid is to be discarded according + * to caller's programs selection + * @param ts : - TS context + * @param pid : - pid + * @return 1 if the pid is only comprised in programs that have .discard=AVDISCARD_ALL + * 0 otherwise + */ +static int discard_pid(MpegTSContext *ts, unsigned int pid) +{ + int i, j, k; + int used = 0, discarded = 0; + struct Program *p; + for(i=0; inb_prg; i++) { + p = &ts->prg[i]; + for(j=0; jnb_pids; j++) { + if(p->pids[j] != pid) + continue; + //is program with id p->id set to be discarded? + for(k=0; kstream->nb_programs; k++) { + if(ts->stream->programs[k]->id == p->id) { + if(ts->stream->programs[k]->discard == AVDISCARD_ALL) + discarded++; + else + used++; + } + } + } + } + + return !used && discarded; +} + +//ugly forward declaration +static void mpegts_push_section(MpegTSFilter *filter, const uint8_t *section, int section_len); + +/** \fn write_section_data(AVFormatContext*,MpegTSFilter*,const uint8_t*,int,int) + * Assemble PES packets out of TS packets, and then call the "section_cb" + * function when they are complete. + * + * NOTE: "DVB Section" is DVB terminology for an MPEG PES packet. + */ +static void write_section_data(AVFormatContext *s, MpegTSFilter *tss1, + const uint8_t *buf, int buf_size, int is_start) +{ + MpegTSSectionFilter *tss = &tss1->u.section_filter; + int len; + + assert(tss->section_buf); + + if (is_start) { + memcpy(tss->section_buf, buf, buf_size); + tss->section_index = buf_size; + tss->section_h_size = -1; + tss->end_of_section_reached = 0; + } else { + if (tss->end_of_section_reached) + return; + len = 4096 - tss->section_index; + if (buf_size < len) + len = buf_size; + memcpy(tss->section_buf + tss->section_index, buf, len); + tss->section_index += len; + } + + if (tss->section_cb == mpegts_push_section) { + SectionContext *sect = tss->opaque; + sect->new_packet = 1; + } + while (!tss->end_of_section_reached) { + /* compute section length if possible */ + if (tss->section_h_size == -1 && tss->section_index >= 3) { + len = (AV_RB16(tss->section_buf + 1) & 0xfff) + 3; + if (len > 4096) + return; + tss->section_h_size = len; + } + + if (tss->section_h_size == -1 || + tss->section_index < tss->section_h_size) + { + if (tss1->pmt_chop_at_ts && tss->section_buf[0] == PMT_TID) + { + /* HACK! To allow BBC IPTV streams with incomplete PMTs (they + * advertise a length of 383, but only send 182 bytes!), we + * will not wait for the remainder of the PMT, but accept just + * what is in the first TS payload, as this is enough to get + * playback, although some PIDs may be filtered out as a result + */ + tss->section_h_size = tss->section_index; + } + else + break; + } + + if (!tss->check_crc || + av_crc(av_crc_get_table(AV_CRC_32_IEEE), -1, + tss->section_buf, tss->section_h_size) == 0) + tss->section_cb(tss1, tss->section_buf, tss->section_h_size); + else + av_log(s, AV_LOG_WARNING, "write_section_data: PID %#x CRC error\n", tss1->pid); + + if (tss->section_index > tss->section_h_size) { + int left = tss->section_index - tss->section_h_size; + memmove(tss->section_buf, tss->section_buf+tss->section_h_size, + left); + tss->section_index = left; + tss->section_h_size = -1; + } else { + tss->end_of_section_reached = 1; + } + } +} + +static MpegTSFilter *mpegts_open_section_filter(MpegTSContext *ts, unsigned int pid, + SectionCallback *section_cb, void *opaque, + int check_crc) + +{ + MpegTSFilter *filter = ts->pids[pid]; + MpegTSSectionFilter *sec; + + av_dlog(ts->stream, "Filter: pid=0x%x\n", pid); + + if (filter) { +#ifdef DEBUG + av_log(ts->stream, AV_LOG_DEBUG, "Filter Already Exists\n"); +#endif + mpegts_close_filter(ts, filter); + } + + if (pid >= NB_PID_MAX || ts->pids[pid]) + return NULL; + filter = av_mallocz(sizeof(MpegTSFilter)); + if (!filter) + return NULL; + ts->pids[pid] = filter; + filter->type = MPEGTS_SECTION; + filter->pid = pid; + filter->es_id = -1; + filter->last_cc = -1; + filter->pmt_chop_at_ts = 0; + sec = &filter->u.section_filter; + sec->section_cb = section_cb; + sec->opaque = opaque; + sec->section_buf = av_malloc(MAX_SECTION_SIZE); + sec->check_crc = check_crc; + if (!sec->section_buf) { + av_free(filter); + return NULL; + } + return filter; +} + +static MpegTSFilter *mpegts_open_pes_filter(MpegTSContext *ts, unsigned int pid, + PESCallback *pes_cb, + void *opaque) +{ + MpegTSFilter *filter; + MpegTSPESFilter *pes; + + if (pid >= NB_PID_MAX || ts->pids[pid]) + return NULL; + filter = av_mallocz(sizeof(MpegTSFilter)); + if (!filter) + return NULL; + ts->pids[pid] = filter; + filter->type = MPEGTS_PES; + filter->pid = pid; + filter->es_id = -1; + filter->last_cc = -1; + pes = &filter->u.pes_filter; + pes->pes_cb = pes_cb; + pes->opaque = opaque; + return filter; +} + +static void mpegts_close_filter(MpegTSContext *ts, MpegTSFilter *filter) +{ + int pid; + + if (!ts || !filter) + return; + + pid = filter->pid; + +#ifdef DEBUG + av_log(NULL, AV_LOG_DEBUG, "Closing Filter: pid=0x%x\n", pid); +#endif + if (filter == ts->pmt_filter) + { + av_log(NULL, AV_LOG_DEBUG, "Closing PMT Filter: pid=0x%x\n", pid); + ts->pmt_filter = NULL; + } + if (filter == ts->pat_filter) + { + av_log(NULL, AV_LOG_DEBUG, "Closing PAT Filter: pid=0x%x\n", pid); + ts->pat_filter = NULL; + } + + if (filter->type == MPEGTS_SECTION) + av_freep(&filter->u.section_filter.section_buf); + else if (filter->type == MPEGTS_PES) { + PESContext *pes = filter->u.pes_filter.opaque; + av_freep(&pes->buffer); + /* referenced private data will be freed later in + * avformat_close_input */ + if (!((PESContext *)filter->u.pes_filter.opaque)->st) { + av_freep(&filter->u.pes_filter.opaque); + } + } + + av_free(filter); + ts->pids[pid] = NULL; +} + +static int analyze(const uint8_t *buf, int size, int packet_size, int *index){ + int stat[TS_MAX_PACKET_SIZE]; + int i; + int x=0; + int best_score=0; + + memset(stat, 0, packet_size*sizeof(int)); + + for(x=i=0; i best_score){ + best_score= stat[x]; + if(index) *index= x; + } + } + + x++; + if(x == packet_size) x= 0; + } + + return best_score; +} + +/* autodetect fec presence. Must have at least 1024 bytes */ +static int get_packet_size(const uint8_t *buf, int size) +{ + int score, fec_score, dvhs_score; + + if (size < (TS_FEC_PACKET_SIZE * 5 + 1)) + return -1; + + score = analyze(buf, size, TS_PACKET_SIZE, NULL); + dvhs_score = analyze(buf, size, TS_DVHS_PACKET_SIZE, NULL); + fec_score= analyze(buf, size, TS_FEC_PACKET_SIZE, NULL); +// av_log(NULL, AV_LOG_DEBUG, "score: %d, dvhs_score: %d, fec_score: %d \n", score, dvhs_score, fec_score); + + if (score > fec_score && score > dvhs_score) return TS_PACKET_SIZE; + else if(dvhs_score > score && dvhs_score > fec_score) return TS_DVHS_PACKET_SIZE; + else if(score < fec_score && dvhs_score < fec_score) return TS_FEC_PACKET_SIZE; + else return -1; +} + +typedef struct SectionHeader { + uint8_t tid; + uint16_t id; + uint8_t version; + uint8_t sec_num; + uint8_t last_sec_num; +} SectionHeader; + +static inline int get8(const uint8_t **pp, const uint8_t *p_end) +{ + const uint8_t *p; + int c; + + p = *pp; + if (p >= p_end) + return -1; + c = *p++; + *pp = p; + return c; +} + +static inline int get16(const uint8_t **pp, const uint8_t *p_end) +{ + const uint8_t *p; + int c; + + p = *pp; + if ((p + 1) >= p_end) + return -1; + c = AV_RB16(p); + p += 2; + *pp = p; + return c; +} + +/* read and allocate a DVB string preceded by its length */ +static char *getstr8(const uint8_t **pp, const uint8_t *p_end) +{ + int len; + const uint8_t *p; + char *str; + + p = *pp; + len = get8(&p, p_end); + if (len < 0) + return NULL; + if ((p + len) > p_end) + return NULL; + str = av_malloc(len + 1); + if (!str) + return NULL; + memcpy(str, p, len); + str[len] = '\0'; + p += len; + *pp = p; + return str; +} + +static int parse_section_header(SectionHeader *h, + const uint8_t **pp, const uint8_t *p_end) +{ + int val; + + val = get8(pp, p_end); + if (val < 0) + return -1; + h->tid = val; + *pp += 2; + val = get16(pp, p_end); + if (val < 0) + return -1; + h->id = val; + val = get8(pp, p_end); + if (val < 0) + return -1; + h->version = (val >> 1) & 0x1f; + val = get8(pp, p_end); + if (val < 0) + return -1; + h->sec_num = val; + val = get8(pp, p_end); + if (val < 0) + return -1; + h->last_sec_num = val; + +#ifdef DEBUG + av_log(NULL, AV_LOG_DEBUG, "sid=0x%x sec_num=%d/%d\n", + h->id, h->sec_num, h->last_sec_num); +#endif + return 0; +} + +/* mpegts_push_section: return one or more tables. The tables may not completely fill + the packet and there may be stuffing bytes at the end. + This is complicated because a single TS packet may result in several tables being + produced. We may have a "start" bit indicating, in effect, the end of a table but + the rest of the TS packet after the start may be filled with one or more small tables. +*/ +static void mpegts_push_section(MpegTSFilter *filter, const uint8_t *section, int section_len) +{ + SectionContext *sect = filter->u.section_filter.opaque; + MpegTSContext *ts = sect->ts; + SectionHeader header; + AVPacket *pkt = ts->pkt; + const uint8_t *p = section, *p_end = section + section_len - 4; + + if (parse_section_header(&header, &p, p_end) < 0) + { + av_log(NULL, AV_LOG_DEBUG, "Unable to parse header\n"); + return; + } + + if (sect->new_packet && pkt && sect->st && pkt->size == 0) { + int pktLen = section_len + 184; /* Add enough for a complete TS payload. */ + sect->new_packet = 0; + av_free_packet(pkt); + if (av_new_packet(pkt, pktLen) == 0) { + memcpy(pkt->data, section, section_len); + memset(pkt->data+section_len, 0xff, pktLen-section_len); + pkt->stream_index = sect->st->index; + ts->stop_parse = 1; + } + } else if (pkt->data) { /* We've already added at least one table. */ + uint8_t *data = pkt->data; + int space = pkt->size; + int table_size = 0; + while (space > 3 + table_size) { + table_size = (((data[1] & 0xf) << 8) | data[2]) + 3; + if (table_size < space) { + space -= table_size; + data += table_size; + } /* Otherwise we've got filler. */ + } + if (space < section_len) { + av_log(NULL, AV_LOG_DEBUG, "Insufficient space for additional packet\n"); + return; + } + memcpy(data, section, section_len); + } +} + +typedef struct { + uint32_t stream_type; + enum AVMediaType codec_type; + enum AVCodecID codec_id; +} StreamType; + +static const StreamType ISO_types[] = { + { 0x01, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO }, + { 0x02, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO }, + { 0x03, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3 }, + { 0x04, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3 }, + { 0x0b, AVMEDIA_TYPE_DATA, AV_CODEC_ID_DSMCC_B }, /* DVB_CAROUSEL_ID */ + { 0x0f, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC }, + { 0x10, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 }, + { 0x11, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC_LATM }, /* LATM syntax */ + { 0x1b, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 }, + { 0xd1, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC }, + { 0xea, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 }, + { 0 }, +}; + +static const StreamType HDMV_types[] = { + { 0x80, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_BLURAY }, + { 0x81, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 }, + { 0x82, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, + { 0x83, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_TRUEHD }, + { 0x84, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3 }, + { 0x85, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, /* DTS HD */ + { 0x86, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, /* DTS HD MASTER*/ + { 0xa1, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3 }, /* E-AC3 Secondary Audio */ + { 0xa2, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, /* DTS Express Secondary Audio */ + { 0x90, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_HDMV_PGS_SUBTITLE }, + { 0 }, +}; + +/* ATSC ? */ +static const StreamType MISC_types[] = { + { 0x81, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 }, + { 0x87, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3 }, + { 0x8a, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, + { 0x100, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_DVB_SUBTITLE }, + { 0x101, AVMEDIA_TYPE_DATA, AV_CODEC_ID_DVB_VBI }, + { 0 }, +}; + +static const StreamType REGD_types[] = { + { MKTAG('d','r','a','c'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC }, + { MKTAG('A','C','-','3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 }, + { MKTAG('B','S','S','D'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_S302M }, + { MKTAG('D','T','S','1'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, + { MKTAG('D','T','S','2'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, + { MKTAG('D','T','S','3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, + { MKTAG('V','C','-','1'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 }, + { 0 }, +}; + +/* descriptor present */ +static const StreamType DESC_types[] = { + { 0x6a, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 }, /* AC-3 descriptor */ + { 0x7a, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3 }, /* E-AC-3 descriptor */ + { 0x7b, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, + { 0x13, AVMEDIA_TYPE_DATA, AV_CODEC_ID_DSMCC_B }, /* DVB_CAROUSEL_ID */ + { 0x45, AVMEDIA_TYPE_DATA, AV_CODEC_ID_DVB_VBI }, /* DVB_VBI_DATA_ID */ + { 0x46, AVMEDIA_TYPE_DATA, AV_CODEC_ID_DVB_VBI }, /* DVB_VBI_TELETEXT_ID */ //FixMe type subtilte + { 0x56, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_DVB_TELETEXT }, + { 0x59, AVMEDIA_TYPE_SUBTITLE, AV_CODEC_ID_DVB_SUBTITLE }, /* subtitling descriptor */ + { 0 }, +}; + +/* component tags */ +static const StreamType COMPONENT_TAG_types[] = { + { 0x0a, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3 }, + { 0x52, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO }, +}; + +static void mpegts_find_stream_type(AVStream *st, + uint32_t stream_type, const StreamType *types) +{ + for (; types->stream_type; types++) { + if (stream_type == types->stream_type) { + st->codec->codec_type = types->codec_type; + st->codec->codec_id = types->codec_id; + st->request_probe = 0; + return; + } + } +} + +static void mpegts_find_stream_type_pmt(pmt_entry_t *st, + uint32_t stream_type, const StreamType *types) +{ + for (; types->stream_type; types++) { + if (stream_type == types->stream_type) { + st->codec_type = types->codec_type; + st->codec_id = types->codec_id; + return; + } + } +} + +static int mpegts_set_stream_info(AVStream *st, PESContext *pes, + uint32_t stream_type, uint32_t prog_reg_desc) +{ + int old_codec_type= st->codec->codec_type; + int old_codec_id = st->codec->codec_id; + avpriv_set_pts_info(st, 33, 1, 90000); + st->priv_data = pes; + st->codec->codec_type = AVMEDIA_TYPE_DATA; + st->codec->codec_id = AV_CODEC_ID_NONE; + st->need_parsing = AVSTREAM_PARSE_FULL; + pes->st = st; + pes->stream_type = stream_type; + + av_log(pes->stream, AV_LOG_DEBUG, + "stream=%d stream_type=%x pid=%x prog_reg_desc=%.4s\n", + st->index, pes->stream_type, pes->pid, (char*)&prog_reg_desc); + + st->codec->codec_tag = pes->stream_type; + + mpegts_find_stream_type(st, pes->stream_type, ISO_types); + if ((prog_reg_desc == AV_RL32("HDMV") || + prog_reg_desc == AV_RL32("HDPR")) && + st->codec->codec_id == AV_CODEC_ID_NONE) { + mpegts_find_stream_type(st, pes->stream_type, HDMV_types); + if (pes->stream_type == 0x83) { + // HDMV TrueHD streams also contain an AC3 coded version of the + // audio track - add a second stream for this + AVStream *sub_st; + // priv_data cannot be shared between streams + PESContext *sub_pes = av_malloc(sizeof(*sub_pes)); + if (!sub_pes) + return AVERROR(ENOMEM); + memcpy(sub_pes, pes, sizeof(*sub_pes)); + + sub_st = avformat_new_stream(pes->stream, NULL); + if (!sub_st) { + av_free(sub_pes); + return AVERROR(ENOMEM); + } + + sub_st->id = pes->pid; + avpriv_set_pts_info(sub_st, 33, 1, 90000); + sub_st->priv_data = sub_pes; + sub_st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + sub_st->codec->codec_id = AV_CODEC_ID_AC3; + sub_st->need_parsing = AVSTREAM_PARSE_FULL; + sub_pes->sub_st = pes->sub_st = sub_st; + } + } + if (st->codec->codec_id == AV_CODEC_ID_NONE) + mpegts_find_stream_type(st, pes->stream_type, MISC_types); + if (st->codec->codec_id == AV_CODEC_ID_NONE){ + st->codec->codec_id = old_codec_id; + st->codec->codec_type= old_codec_type; + } + + return 0; +} + +static void new_pes_packet(PESContext *pes, AVPacket *pkt) +{ + av_init_packet(pkt); + + pkt->destruct = av_destruct_packet; + pkt->data = pes->buffer; + pkt->size = pes->data_index; + + if(pes->total_size != MAX_PES_PAYLOAD && + pes->pes_header_size + pes->data_index != pes->total_size + PES_START_SIZE) { + av_log(pes->stream, AV_LOG_WARNING, "PES packet size mismatch\n"); + pes->flags |= AV_PKT_FLAG_CORRUPT; + } + memset(pkt->data+pkt->size, 0, FF_INPUT_BUFFER_PADDING_SIZE); + + // Separate out the AC3 substream from an HDMV combined TrueHD/AC3 PID + if (pes->sub_st && pes->stream_type == 0x83 && pes->extended_stream_id == 0x76) + pkt->stream_index = pes->sub_st->index; + else + pkt->stream_index = pes->st->index; + pkt->pts = pes->pts; + pkt->dts = pes->dts; + /* store position of first TS packet of this PES packet */ + pkt->pos = pes->ts_packet_pos; + pkt->flags = pes->flags; + + /* reset pts values */ + pes->pts = AV_NOPTS_VALUE; + pes->dts = AV_NOPTS_VALUE; + pes->buffer = NULL; + pes->data_index = 0; + pes->flags = 0; +} + +static int read_sl_header(PESContext *pes, SLConfigDescr *sl, const uint8_t *buf, int buf_size) +{ + GetBitContext gb; + int au_start_flag = 0, au_end_flag = 0, ocr_flag = 0, idle_flag = 0; + int padding_flag = 0, padding_bits = 0, inst_bitrate_flag = 0; + int dts_flag = -1, cts_flag = -1; + int64_t dts = AV_NOPTS_VALUE, cts = AV_NOPTS_VALUE; + + init_get_bits(&gb, buf, buf_size*8); + + if (sl->use_au_start) + au_start_flag = get_bits1(&gb); + if (sl->use_au_end) + au_end_flag = get_bits1(&gb); + if (!sl->use_au_start && !sl->use_au_end) + au_start_flag = au_end_flag = 1; + if (sl->ocr_len > 0) + ocr_flag = get_bits1(&gb); + if (sl->use_idle) + idle_flag = get_bits1(&gb); + if (sl->use_padding) + padding_flag = get_bits1(&gb); + if (padding_flag) + padding_bits = get_bits(&gb, 3); + + if (!idle_flag && (!padding_flag || padding_bits != 0)) { + if (sl->packet_seq_num_len) + skip_bits_long(&gb, sl->packet_seq_num_len); + if (sl->degr_prior_len) + if (get_bits1(&gb)) + skip_bits(&gb, sl->degr_prior_len); + if (ocr_flag) + skip_bits_long(&gb, sl->ocr_len); + if (au_start_flag) { + if (sl->use_rand_acc_pt) + get_bits1(&gb); + if (sl->au_seq_num_len > 0) + skip_bits_long(&gb, sl->au_seq_num_len); + if (sl->use_timestamps) { + dts_flag = get_bits1(&gb); + cts_flag = get_bits1(&gb); + } + } + if (sl->inst_bitrate_len) + inst_bitrate_flag = get_bits1(&gb); + if (dts_flag == 1) + dts = get_bits64(&gb, sl->timestamp_len); + if (cts_flag == 1) + cts = get_bits64(&gb, sl->timestamp_len); + if (sl->au_len > 0) + skip_bits_long(&gb, sl->au_len); + if (inst_bitrate_flag) + skip_bits_long(&gb, sl->inst_bitrate_len); + } + + if (dts != AV_NOPTS_VALUE) + pes->dts = dts; + if (cts != AV_NOPTS_VALUE) + pes->pts = cts; + + if (sl->timestamp_len && sl->timestamp_res) + avpriv_set_pts_info(pes->st, sl->timestamp_len, 1, sl->timestamp_res); + + return (get_bits_count(&gb) + 7) >> 3; +} + +/* return non zero if a packet could be constructed */ +static int mpegts_push_data(MpegTSFilter *filter, + const uint8_t *buf, int buf_size, int is_start, + int64_t pos) +{ + PESContext *pes = filter->u.pes_filter.opaque; + MpegTSContext *ts = pes->ts; + const uint8_t *p; + int len, code; + + if(!ts || !ts->pkt) + return 0; + + if (is_start) { + if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) { + new_pes_packet(pes, ts->pkt); + ts->stop_parse = 1; + } + pes->state = MPEGTS_HEADER; + pes->data_index = 0; + pes->ts_packet_pos = pos; + } + p = buf; + while (buf_size > 0) { + switch(pes->state) { + case MPEGTS_HEADER: + len = PES_START_SIZE - pes->data_index; + if (len > buf_size) + len = buf_size; + memcpy(pes->header + pes->data_index, p, len); + pes->data_index += len; + p += len; + buf_size -= len; + if (pes->data_index == PES_START_SIZE) { + /* we got all the PES or section header. We can now + decide */ + if (pes->header[0] == 0x00 && pes->header[1] == 0x00 && + pes->header[2] == 0x01) { + /* it must be an mpeg2 PES stream */ + code = pes->header[3] | 0x100; + av_dlog(pes->stream, "pid=%x pes_code=%#x\n", pes->pid, code); + + if ((pes->st && pes->st->discard == AVDISCARD_ALL && + (!pes->sub_st || pes->sub_st->discard == AVDISCARD_ALL)) || + code == 0x1be) /* padding_stream */ + goto skip; + + /* stream not present in PMT */ + if (!pes->st) { + pes->st = avformat_new_stream(ts->stream, NULL); + if (!pes->st) + return AVERROR(ENOMEM); + pes->st->id = pes->pid; + mpegts_set_stream_info(pes->st, pes, 0, 0); + } + + pes->total_size = AV_RB16(pes->header + 4); + /* NOTE: a zero total size means the PES size is + unbounded */ + if (!pes->total_size) + pes->total_size = MAX_PES_PAYLOAD; + + /* allocate pes buffer */ + pes->buffer = av_malloc(pes->total_size+FF_INPUT_BUFFER_PADDING_SIZE); + if (!pes->buffer) + return AVERROR(ENOMEM); + + if (code != 0x1bc && code != 0x1bf && /* program_stream_map, private_stream_2 */ + code != 0x1f0 && code != 0x1f1 && /* ECM, EMM */ + code != 0x1ff && code != 0x1f2 && /* program_stream_directory, DSMCC_stream */ + code != 0x1f8) { /* ITU-T Rec. H.222.1 type E stream */ + pes->state = MPEGTS_PESHEADER; + if (pes->st->codec->codec_id == AV_CODEC_ID_NONE && !pes->st->request_probe) { + av_dlog(pes->stream, "pid=%x stream_type=%x probing\n", + pes->pid, pes->stream_type); + pes->st->request_probe= 1; + } + } else { + pes->state = MPEGTS_PAYLOAD; + pes->data_index = 0; + } + } else { + /* otherwise, it should be a table */ + /* skip packet */ + skip: + pes->state = MPEGTS_SKIP; + continue; + } + } + break; + /**********************************************/ + /* PES packing parsing */ + case MPEGTS_PESHEADER: + len = PES_HEADER_SIZE - pes->data_index; + if (len < 0) + return -1; + if (len > buf_size) + len = buf_size; + memcpy(pes->header + pes->data_index, p, len); + pes->data_index += len; + p += len; + buf_size -= len; + if (pes->data_index == PES_HEADER_SIZE) { + pes->pes_header_size = pes->header[8] + 9; + pes->state = MPEGTS_PESHEADER_FILL; + } + break; + case MPEGTS_PESHEADER_FILL: + len = pes->pes_header_size - pes->data_index; + if (len < 0) + return -1; + if (len > buf_size) + len = buf_size; + memcpy(pes->header + pes->data_index, p, len); + pes->data_index += len; + p += len; + buf_size -= len; + if (pes->data_index == pes->pes_header_size) { + const uint8_t *r; + unsigned int flags, pes_ext, skip; + + flags = pes->header[7]; + r = pes->header + 9; + pes->pts = AV_NOPTS_VALUE; + pes->dts = AV_NOPTS_VALUE; + if ((flags & 0xc0) == 0x80) { + pes->dts = pes->pts = ff_parse_pes_pts(r); + r += 5; + } else if ((flags & 0xc0) == 0xc0) { + pes->pts = ff_parse_pes_pts(r); + r += 5; + pes->dts = ff_parse_pes_pts(r); + r += 5; + } + pes->extended_stream_id = -1; + if (flags & 0x01) { /* PES extension */ + pes_ext = *r++; + /* Skip PES private data, program packet sequence counter and P-STD buffer */ + skip = (pes_ext >> 4) & 0xb; + skip += skip & 0x9; + r += skip; + if ((pes_ext & 0x41) == 0x01 && + (r + 2) <= (pes->header + pes->pes_header_size)) { + /* PES extension 2 */ + if ((r[0] & 0x7f) > 0 && (r[1] & 0x80) == 0) + pes->extended_stream_id = r[1]; + } + } + + /* we got the full header. We parse it and get the payload */ + pes->state = MPEGTS_PAYLOAD; + pes->data_index = 0; + if (pes->stream_type == 0x12 && buf_size > 0) { + int sl_header_bytes = read_sl_header(pes, &pes->sl, p, buf_size); + pes->pes_header_size += sl_header_bytes; + p += sl_header_bytes; + buf_size -= sl_header_bytes; + } + } + break; + case MPEGTS_PAYLOAD: + if (buf_size > 0 && pes->buffer) { + if (pes->data_index > 0 && pes->data_index+buf_size > pes->total_size) { + new_pes_packet(pes, ts->pkt); + pes->total_size = MAX_PES_PAYLOAD; + pes->buffer = av_malloc(pes->total_size+FF_INPUT_BUFFER_PADDING_SIZE); + if (!pes->buffer) + return AVERROR(ENOMEM); + ts->stop_parse = 1; + } else if (pes->data_index == 0 && buf_size > pes->total_size) { + // pes packet size is < ts size packet and pes data is padded with 0xff + // not sure if this is legal in ts but see issue #2392 + buf_size = pes->total_size; + } + memcpy(pes->buffer+pes->data_index, p, buf_size); + pes->data_index += buf_size; + } + buf_size = 0; + /* emit complete packets with known packet size + * decreases demuxer delay for infrequent packets like subtitles from + * a couple of seconds to milliseconds for properly muxed files. + * total_size is the number of bytes following pes_packet_length + * in the pes header, i.e. not counting the first PES_START_SIZE bytes */ + if (!ts->stop_parse && pes->total_size < MAX_PES_PAYLOAD && + pes->pes_header_size + pes->data_index == pes->total_size + PES_START_SIZE) { + ts->stop_parse = 1; + new_pes_packet(pes, ts->pkt); + } + break; + case MPEGTS_SKIP: + buf_size = 0; + break; + } + } + + return 0; +} + +static PESContext *add_pes_stream(MpegTSContext *ts, int pid, int pcr_pid) +{ + MpegTSFilter *tss = ts->pids[pid]; + PESContext *pes = 0; + if (tss) { /* filter already exists */ + if (tss->type == MPEGTS_PES) + pes = (PESContext*) tss->u.pes_filter.opaque; + /* otherwise, kill it, and start a new stream */ + mpegts_close_filter(ts, tss); + } + + /* create a PES context */ + if (!(pes=av_mallocz(sizeof(PESContext)))) { + av_log(NULL, AV_LOG_ERROR, "Error: av_mallocz() failed in add_pes_stream"); + return 0; + } + pes->ts = ts; + pes->stream = ts->stream; + pes->pid = pid; + pes->pcr_pid = pcr_pid; + pes->state = MPEGTS_SKIP; + pes->pts = AV_NOPTS_VALUE; + pes->dts = AV_NOPTS_VALUE; + tss = mpegts_open_pes_filter(ts, pid, mpegts_push_data, pes); + if (!tss) { + av_free(pes); + av_log(NULL, AV_LOG_ERROR, "Error: unable to open " + "mpegts PES filter in add_pes_stream"); + return 0; + } + return pes; +} + +#define MAX_LEVEL 4 +typedef struct { + AVFormatContext *s; + AVIOContext pb; + Mp4Descr *descr; + Mp4Descr *active_descr; + int descr_count; + int max_descr_count; + int level; +} MP4DescrParseContext; + +static int init_MP4DescrParseContext( + MP4DescrParseContext *d, AVFormatContext *s, const uint8_t *buf, + unsigned size, Mp4Descr *descr, int max_descr_count) +{ + int ret; + if (size > (1<<30)) + return AVERROR_INVALIDDATA; + + if ((ret = ffio_init_context(&d->pb, (unsigned char*)buf, size, 0, + NULL, NULL, NULL, NULL)) < 0) + return ret; + + d->s = s; + d->level = 0; + d->descr_count = 0; + d->descr = descr; + d->active_descr = NULL; + d->max_descr_count = max_descr_count; + + return 0; +} + +static void update_offsets(AVIOContext *pb, int64_t *off, int *len) { + int64_t new_off = avio_tell(pb); + (*len) -= new_off - *off; + *off = new_off; +} + +static int parse_mp4_descr(MP4DescrParseContext *d, int64_t off, int len, + int target_tag); + +static int parse_mp4_descr_arr(MP4DescrParseContext *d, int64_t off, int len) +{ + while (len > 0) { + if (parse_mp4_descr(d, off, len, 0) < 0) + return -1; + update_offsets(&d->pb, &off, &len); + } + return 0; +} + +static int parse_MP4IODescrTag(MP4DescrParseContext *d, int64_t off, int len) +{ + avio_rb16(&d->pb); // ID + avio_r8(&d->pb); + avio_r8(&d->pb); + avio_r8(&d->pb); + avio_r8(&d->pb); + avio_r8(&d->pb); + update_offsets(&d->pb, &off, &len); + return parse_mp4_descr_arr(d, off, len); +} + +static int parse_MP4ODescrTag(MP4DescrParseContext *d, int64_t off, int len) +{ + int id_flags; + if (len < 2) + return 0; + id_flags = avio_rb16(&d->pb); + if (!(id_flags & 0x0020)) { //URL_Flag + update_offsets(&d->pb, &off, &len); + return parse_mp4_descr_arr(d, off, len); //ES_Descriptor[] + } else { + return 0; + } +} + +static int parse_MP4ESDescrTag(MP4DescrParseContext *d, int64_t off, int len) +{ + int es_id = 0; + if (d->descr_count >= d->max_descr_count) + return -1; + ff_mp4_parse_es_descr(&d->pb, &es_id); + d->active_descr = d->descr + (d->descr_count++); + + d->active_descr->es_id = es_id; + update_offsets(&d->pb, &off, &len); + parse_mp4_descr(d, off, len, MP4DecConfigDescrTag); + update_offsets(&d->pb, &off, &len); + if (len > 0) + parse_mp4_descr(d, off, len, MP4SLDescrTag); + d->active_descr = NULL; + return 0; +} + +static int parse_MP4DecConfigDescrTag(MP4DescrParseContext *d, int64_t off, int len) +{ + Mp4Descr *descr = d->active_descr; + if (!descr) + return -1; + d->active_descr->dec_config_descr = av_malloc(len); + if (!descr->dec_config_descr) + return AVERROR(ENOMEM); + descr->dec_config_descr_len = len; + avio_read(&d->pb, descr->dec_config_descr, len); + return 0; +} + +static int parse_MP4SLDescrTag(MP4DescrParseContext *d, int64_t off, int len) +{ + Mp4Descr *descr = d->active_descr; + int predefined; + if (!descr) + return -1; + + predefined = avio_r8(&d->pb); + if (!predefined) { + int lengths; + int flags = avio_r8(&d->pb); + descr->sl.use_au_start = !!(flags & 0x80); + descr->sl.use_au_end = !!(flags & 0x40); + descr->sl.use_rand_acc_pt = !!(flags & 0x20); + descr->sl.use_padding = !!(flags & 0x08); + descr->sl.use_timestamps = !!(flags & 0x04); + descr->sl.use_idle = !!(flags & 0x02); + descr->sl.timestamp_res = avio_rb32(&d->pb); + avio_rb32(&d->pb); + descr->sl.timestamp_len = avio_r8(&d->pb); + descr->sl.ocr_len = avio_r8(&d->pb); + descr->sl.au_len = avio_r8(&d->pb); + descr->sl.inst_bitrate_len = avio_r8(&d->pb); + lengths = avio_rb16(&d->pb); + descr->sl.degr_prior_len = lengths >> 12; + descr->sl.au_seq_num_len = (lengths >> 7) & 0x1f; + descr->sl.packet_seq_num_len = (lengths >> 2) & 0x1f; + } else { + av_log_missing_feature(d->s, "Predefined SLConfigDescriptor", 0); + } + return 0; +} + +static int parse_mp4_descr(MP4DescrParseContext *d, int64_t off, int len, + int target_tag) { + int tag; + int len1 = ff_mp4_read_descr(d->s, &d->pb, &tag); + update_offsets(&d->pb, &off, &len); + if (len < 0 || len1 > len || len1 <= 0) { + av_log(d->s, AV_LOG_ERROR, "Tag %x length violation new length %d bytes remaining %d\n", tag, len1, len); + return -1; + } + + if (d->level++ >= MAX_LEVEL) { + av_log(d->s, AV_LOG_ERROR, "Maximum MP4 descriptor level exceeded\n"); + goto done; + } + + if (target_tag && tag != target_tag) { + av_log(d->s, AV_LOG_ERROR, "Found tag %x expected %x\n", tag, target_tag); + goto done; + } + + switch (tag) { + case MP4IODescrTag: + parse_MP4IODescrTag(d, off, len1); + break; + case MP4ODescrTag: + parse_MP4ODescrTag(d, off, len1); + break; + case MP4ESDescrTag: + parse_MP4ESDescrTag(d, off, len1); + break; + case MP4DecConfigDescrTag: + parse_MP4DecConfigDescrTag(d, off, len1); + break; + case MP4SLDescrTag: + parse_MP4SLDescrTag(d, off, len1); + break; + } + +done: + d->level--; + avio_seek(&d->pb, off + len1, SEEK_SET); + return 0; +} + +static int mp4_read_iods(AVFormatContext *s, const uint8_t *buf, unsigned size, + Mp4Descr *descr, int *descr_count, int max_descr_count) +{ + MP4DescrParseContext d; + if (init_MP4DescrParseContext(&d, s, buf, size, descr, max_descr_count) < 0) + return -1; + + parse_mp4_descr(&d, avio_tell(&d.pb), size, MP4IODescrTag); + + *descr_count = d.descr_count; + return 0; +} + +static int mp4_read_od(AVFormatContext *s, const uint8_t *buf, unsigned size, + Mp4Descr *descr, int *descr_count, int max_descr_count) +{ + MP4DescrParseContext d; + if (init_MP4DescrParseContext(&d, s, buf, size, descr, max_descr_count) < 0) + return -1; + + parse_mp4_descr_arr(&d, avio_tell(&d.pb), size); + + *descr_count = d.descr_count; + return 0; +} + +static void m4sl_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) +{ + MpegTSContext *ts = filter->u.section_filter.opaque; + SectionHeader h; + const uint8_t *p, *p_end; + AVIOContext pb; + Mp4Descr mp4_descr[MAX_MP4_DESCR_COUNT] = {{ 0 }}; + int mp4_descr_count = 0; + int i, pid; + AVFormatContext *s = ts->stream; + + p_end = section + section_len - 4; + p = section; + if (parse_section_header(&h, &p, p_end) < 0) + return; + if (h.tid != M4OD_TID) + return; + + mp4_read_od(s, p, (unsigned)(p_end - p), mp4_descr, &mp4_descr_count, MAX_MP4_DESCR_COUNT); + + for (pid = 0; pid < NB_PID_MAX; pid++) { + if (!ts->pids[pid]) + continue; + for (i = 0; i < mp4_descr_count; i++) { + PESContext *pes; + AVStream *st; + if (ts->pids[pid]->es_id != mp4_descr[i].es_id) + continue; + if (!(ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES)) { + av_log(s, AV_LOG_ERROR, "pid %x is not PES\n", pid); + continue; + } + pes = ts->pids[pid]->u.pes_filter.opaque; + st = pes->st; + if (!st) { + continue; + } + + pes->sl = mp4_descr[i].sl; + + ffio_init_context(&pb, mp4_descr[i].dec_config_descr, + mp4_descr[i].dec_config_descr_len, 0, NULL, NULL, NULL, NULL); + ff_mp4_read_dec_config_descr(s, st, &pb); + if (st->codec->codec_id == AV_CODEC_ID_AAC && + st->codec->extradata_size > 0) + st->need_parsing = 0; + if (st->codec->codec_id == AV_CODEC_ID_H264 && + st->codec->extradata_size > 0) + st->need_parsing = 0; + + if (st->codec->codec_id <= AV_CODEC_ID_NONE) { + } else if (st->codec->codec_id < AV_CODEC_ID_FIRST_AUDIO) { + st->codec->codec_type = AVMEDIA_TYPE_VIDEO; + } else if (st->codec->codec_id < AV_CODEC_ID_FIRST_SUBTITLE) { + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + } else if (st->codec->codec_id < AV_CODEC_ID_FIRST_UNKNOWN) { + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + } + } + } + for (i = 0; i < mp4_descr_count; i++) + av_free(mp4_descr[i].dec_config_descr); +} + +int ff_parse_mpeg2_descriptor(AVFormatContext *fc, pmt_entry_t *item, int stream_type, + const uint8_t **pp, const uint8_t *desc_list_end, + Mp4Descr *mp4_descr, int mp4_descr_count, int pid, + MpegTSContext *ts, dvb_caption_info_t *dvbci) +{ + const uint8_t *desc_end; + int desc_len, desc_tag, desc_es_id; + char *language; + int i; + + desc_tag = get8(pp, desc_list_end); + if (desc_tag < 0) + return -1; + desc_len = get8(pp, desc_list_end); + if (desc_len < 0) + return -1; + desc_end = *pp + desc_len; + if (desc_end > desc_list_end) + return -1; + + av_dlog(fc, "tag: 0x%02x len=%d\n", desc_tag, desc_len); + + if (item->codec_id == AV_CODEC_ID_NONE && + stream_type == STREAM_TYPE_PRIVATE_DATA) + { + mpegts_find_stream_type_pmt(item, desc_tag, DESC_types); + + if (item->codec_id != AV_CODEC_ID_NONE) + stream_type = 0; + } + + language = dvbci->language; + + switch(desc_tag) { +#if 0 + case 0x1E: /* SL descriptor */ + desc_es_id = get16(pp, desc_end); + if (ts && ts->pids[pid]) + ts->pids[pid]->es_id = desc_es_id; + for (i = 0; i < mp4_descr_count; i++) + if (mp4_descr[i].dec_config_descr_len && + mp4_descr[i].es_id == desc_es_id) { + AVIOContext pb; + ffio_init_context(&pb, mp4_descr[i].dec_config_descr, + mp4_descr[i].dec_config_descr_len, 0, NULL, NULL, NULL, NULL); + ff_mp4_read_dec_config_descr(fc, st, &pb); + if (item->codec_id == AV_CODEC_ID_AAC && + st->codec->extradata_size > 0) + st->need_parsing = 0; + if (item->codec_id == AV_CODEC_ID_MPEG4SYSTEMS) + mpegts_open_section_filter(ts, pid, m4sl_cb, ts, 1); + } + break; + case 0x1F: /* FMC descriptor */ + get16(pp, desc_end); + if (mp4_descr_count > 0 && (item->codec_id == AV_CODEC_ID_AAC_LATM || st->request_probe>0) && + mp4_descr->dec_config_descr_len && mp4_descr->es_id == pid) { + AVIOContext pb; + ffio_init_context(&pb, mp4_descr->dec_config_descr, + mp4_descr->dec_config_descr_len, 0, NULL, NULL, NULL, NULL); + ff_mp4_read_dec_config_descr(fc, st, &pb); + if (item->codec_id == AV_CODEC_ID_AAC && + st->codec->extradata_size > 0){ + st->request_probe= st->need_parsing = 0; + st->codec->codec_type= AVMEDIA_TYPE_AUDIO; + } + } + break; +#endif + case 0x56: /* DVB teletext descriptor */ + language[0] = get8(pp, desc_end); + language[1] = get8(pp, desc_end); + language[2] = get8(pp, desc_end); + language[3] = 0; + break; + case 0x59: /* subtitling descriptor */ + language[0] = get8(pp, desc_end); + language[1] = get8(pp, desc_end); + language[2] = get8(pp, desc_end); + language[3] = 0; + get8(pp, desc_end); + +#if 0 + /* hearing impaired subtitles detection */ + switch(get8(pp, desc_end)) { + case 0x20: /* DVB subtitles (for the hard of hearing) with no monitor aspect ratio criticality */ + case 0x21: /* DVB subtitles (for the hard of hearing) for display on 4:3 aspect ratio monitor */ + case 0x22: /* DVB subtitles (for the hard of hearing) for display on 16:9 aspect ratio monitor */ + case 0x23: /* DVB subtitles (for the hard of hearing) for display on 2.21:1 aspect ratio monitor */ + case 0x24: /* DVB subtitles (for the hard of hearing) for display on a high definition monitor */ + case 0x25: /* DVB subtitles (for the hard of hearing) with plano-stereoscopic disparity for display on a high definition monitor */ + st->disposition |= AV_DISPOSITION_HEARING_IMPAIRED; + break; + } +#endif + + dvbci->comp_page = get16(pp, desc_end); + dvbci->anc_page = get16(pp, desc_end); + dvbci->sub_id = (dvbci->anc_page << 16) | dvbci->comp_page; + +#if 0 + if (st->codec->extradata) { + if (st->codec->extradata_size == 4 && memcmp(st->codec->extradata, *pp, 4)) + av_log_ask_for_sample(fc, "DVB sub with multiple IDs\n"); + } else { + st->codec->extradata = av_malloc(4 + FF_INPUT_BUFFER_PADDING_SIZE); + if (st->codec->extradata) { + st->codec->extradata_size = 4; + memcpy(st->codec->extradata, *pp, 4); + } + } +#endif + *pp += 4; + break; + case 0x0a: /* ISO 639 language descriptor */ + for (i = 0; i + 4 <= desc_len; i += 4) { + language[i + 0] = get8(pp, desc_end); + language[i + 1] = get8(pp, desc_end); + language[i + 2] = get8(pp, desc_end); + language[i + 3] = ','; +#if 0 + switch (get8(pp, desc_end)) { + case 0x01: st->disposition |= AV_DISPOSITION_CLEAN_EFFECTS; break; + case 0x02: st->disposition |= AV_DISPOSITION_HEARING_IMPAIRED; break; + case 0x03: st->disposition |= AV_DISPOSITION_VISUAL_IMPAIRED; break; + } + } +#else + } + get8(pp, desc_end); +#endif + if (i) { + language[i - 1] = 0; + } + break; + case 0x05: /* registration descriptor */ + dvbci->codec_tag = bytestream_get_le32(pp); + av_dlog(fc, "reg_desc=%.4s\n", (char*)&dvbci->codec_tag); + if (item->codec_id == AV_CODEC_ID_NONE && + stream_type == STREAM_TYPE_PRIVATE_DATA) + mpegts_find_stream_type_pmt(item, dvbci->codec_tag, REGD_types); + break; +#if 0 + case 0x52: /* stream identifier descriptor */ + st->stream_identifier = 1 + get8(pp, desc_end); + break; +#endif + case DVB_BROADCAST_ID: + dvbci->data_id = get16(pp, desc_end); + break; + case DVB_CAROUSEL_ID: + { + int carId = 0; + carId = get8(pp, desc_end); + carId = (carId << 8) | get8(pp, desc_end); + carId = (carId << 8) | get8(pp, desc_end); + carId = (carId << 8) | get8(pp, desc_end); + dvbci->carousel_id = carId; + } + break; + case DVB_DATA_STREAM: + dvbci->component_tag = get8(pp, desc_end); + /* Audio and video are sometimes encoded in private streams labelled with + * a component tag. */ +#if 0 + if (item->codec_id == AV_CODEC_ID_NONE && + desc_count == 1 && + stream_type == STREAM_TYPE_PRIVATE_DATA) + mpegts_find_stream_type_pmt(item, dvbci->component_tag, + COMPONENT_TAG_types); +#endif + break; + case DVB_VBI_TELETEXT_ID: + language[0] = get8(pp, desc_end); + language[1] = get8(pp, desc_end); + language[2] = get8(pp, desc_end); + dvbci->txt_type = (get8(pp, desc_end)) >> 3; + break; + case DVB_VBI_DATA_ID: + dvbci->vbi_data = 1; //not parsing the data service descriptors + break; + default: + break; + } + *pp = desc_end; + return 0; +} + +static int find_in_list(const int *pids, int pid) +{ + int i; + for (i=0; icodec_type) + { + case AVMEDIA_TYPE_VIDEO: + case AVMEDIA_TYPE_AUDIO: + case AVMEDIA_TYPE_SUBTITLE: + val = 1; + break; + case AVMEDIA_TYPE_DATA: + switch (item->codec_id) + { + case AV_CODEC_ID_DSMCC_B: + case AV_CODEC_ID_DVB_VBI: + val = 1; + break; + default: + break; + } + break; + default: + /* we ignore the other streams */ + break; + } + return val; +} + +#define HANDLE_PMT_ERROR(MSG) \ + do { av_log(NULL, AV_LOG_ERROR, MSG); return; } while (0) + +#define HANDLE_PMT_PARSE_ERROR(PMSG) \ + HANDLE_PMT_ERROR("Something went terribly wrong in PMT parsing" \ + " when looking at " PMSG "\n") + +static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) +{ + MpegTSContext *ts = filter->u.section_filter.opaque; + SectionHeader h1, *h = &h1; + + int last_item = 0; + int desc_count = 0; + int streams_changed = 0; + PESContext *pes; + const uint8_t *p, *p_end, *desc_list_end; + int program_info_length, pcr_pid, pid, stream_type; + int desc_list_len; + char *language; + uint32_t prog_reg_desc = 0; /* registration descriptor */ + + Mp4Descr mp4_descr[MAX_MP4_DESCR_COUNT] = {{ 0 }}; + int mp4_descr_count = 0; + int i; + + pmt_entry_t items[PMT_PIDS_MAX]; + memset(&items, 0, sizeof(pmt_entry_t) * PMT_PIDS_MAX); + + // initialize to codec_type_unknown + for (int i=0; i < PMT_PIDS_MAX; i++) + items[i].codec_type = AVMEDIA_TYPE_UNKNOWN; + + mpegts_cleanup_streams(ts); /* in case someone else removed streams.. */ + + av_dlog(ts->stream, "PMT: len %i\n", section_len); + hex_dump_debug(ts->stream, (uint8_t *)section, section_len); + + p_end = section + section_len - 4; + p = section; + if (parse_section_header(h, &p, p_end) < 0) + HANDLE_PMT_PARSE_ERROR("section header"); + + av_dlog(ts->stream, "sid=0x%x sec_num=%d/%d\n", + h->id, h->sec_num, h->last_sec_num); + + /* Check if this is really a PMT, and if so the right one */ + if (h->tid != PMT_TID) + HANDLE_PMT_ERROR("pmt_cb() got a TS packet that doesn't have PMT TID\n"); + + /* if we require a specific PMT, and this isn't it return silently */ + if (ts->req_sid >= 0 && h->id != ts->req_sid) + { +#ifdef DEBUG + av_dlog(ts->stream, "We are looking for program 0x%x, not 0x%x", + ts->req_sid, h->id); +#endif + return; + } + + clear_program(ts, h->id); + pcr_pid = get16(&p, p_end); + if (pcr_pid < 0) + return; + pcr_pid &= 0x1fff; + add_pid_to_pmt(ts, h->id, pcr_pid); + set_pcr_pid(ts->stream, h->id, pcr_pid); + + av_dlog(ts->stream, "pcr_pid=0x%x\n", pcr_pid); + + program_info_length = get16(&p, p_end); + if (program_info_length < 0) + return; + program_info_length &= 0xfff; + while(program_info_length >= 2) { + uint8_t tag, len; + tag = get8(&p, p_end); + len = get8(&p, p_end); + + av_dlog(ts->stream, "program tag: 0x%02x len=%d\n", tag, len); + + if(len > program_info_length - 2) + //something else is broken, exit the program_descriptors_loop + break; + program_info_length -= len + 2; + if (tag == 0x1d) { // IOD descriptor + get8(&p, p_end); // scope + get8(&p, p_end); // label + len -= 2; + mp4_read_iods(ts->stream, p, len, mp4_descr + mp4_descr_count, + &mp4_descr_count, MAX_MP4_DESCR_COUNT); + } else if (tag == 0x05 && len >= 4) { // registration descriptor + prog_reg_desc = bytestream_get_le32(&p); + len -= 4; + } + p += len; + } + p += program_info_length; + if (p >= p_end) + return; + + // stop parsing after pmt, we found header + if (!ts->stream->nb_streams) + ts->stop_parse = 2; + + for(;;) { + dvb_caption_info_t dvbci; + stream_type = get8(&p, p_end); + if (stream_type < 0) + break; + pid = get16(&p, p_end); + if (pid < 0) + break; + pid &= 0x1fff; + + /* break if we are out of space. */ + if (last_item >= PMT_PIDS_MAX) { + av_log(NULL, AV_LOG_DEBUG, + "Could not add new pid 0x%x, i = %i, " + "would cause overrun\n", pid, last_item); + assert(0); + break; + } + + items[last_item].pid = pid; + + mpegts_find_stream_type_pmt(&items[last_item], stream_type, ISO_types); + if (items[last_item].codec_id == AV_CODEC_ID_NONE) { + if (prog_reg_desc == AV_RL32("HDMV")) + mpegts_find_stream_type_pmt(&items[last_item], stream_type, HDMV_types); + else + mpegts_find_stream_type_pmt(&items[last_item], stream_type, MISC_types); + } + + memset(&dvbci, 0, sizeof(dvb_caption_info_t)); + + desc_list_len = get16(&p, p_end); + if (desc_list_len < 0) + break; + desc_list_len &= 0xfff; + desc_list_end = p + desc_list_len; + if (desc_list_end > p_end) + break; + for(;;) { + if (ff_parse_mpeg2_descriptor(ts->stream, &items[last_item], stream_type, &p, desc_list_end, + mp4_descr, mp4_descr_count, pid, ts, &dvbci) < 0) + break; + } + p = desc_list_end; + + if (is_desired_stream(&items[last_item])) { + items[last_item].type = stream_type; + memcpy(&items[last_item].dvbci, &dvbci, + sizeof(dvb_caption_info_t)); + last_item++; + } + + desc_count++; + } + + /* if the pmt has changed delete old streams, + * create new ones, and notify any listener. + */ + if (!is_pmt_same(ts, items, last_item)) + { + AVFormatContext *avctx = ts->stream; + int idx; + /* flush out old AVPackets */ + ff_read_frame_flush(avctx); + /* delete old streams */ + for (idx = ts->pid_cnt-1; idx>=0; idx--) + av_remove_stream(ts->stream, ts->pmt_pids[idx], 1); + + /* create new streams */ + for (idx = 0; idx < last_item; idx++) + mpegts_add_stream(ts, h->id, &items[idx], prog_reg_desc, pcr_pid); + + /* cache pmt */ + void *tmp0 = avctx->cur_pmt_sect; + void *tmp1 = av_malloc(section_len); + memcpy(tmp1, section, section_len); + avctx->cur_pmt_sect = (uint8_t*) tmp1; + avctx->cur_pmt_sect_len = section_len; + if (tmp0) + av_free(tmp0); + + /* notify stream_changed listeners */ + if (avctx->streams_changed) + { + av_log(NULL, AV_LOG_DEBUG, "streams_changed()\n"); + avctx->streams_changed(avctx->stream_change_data); + } + } + + /* if we are scanning, tell scanner we found the PMT */ + if (ts->scanning) + { + ts->pmt_scan_state = PMT_FOUND; + ts->stop_parse = 1; + } +} + +static int is_pat_same(MpegTSContext *mpegts_ctx, + int *pmt_pnums, int *pmt_pids, unsigned int pmt_count) +{ + int idx; + if (mpegts_ctx->nb_prg != pmt_count) + return 0; + + for (idx = 0; idx < pmt_count; idx++) + { + if ((mpegts_ctx->prg[idx].id != pmt_pnums[idx]) || + (mpegts_ctx->prg[idx].pid != pmt_pids[idx])) + return 0; + } + return 1; +} + +static int is_pmt_same(MpegTSContext *mpegts_ctx, + pmt_entry_t* items, int item_cnt) +{ + int idx; + if (mpegts_ctx->pid_cnt != item_cnt) + { +#ifdef DEBUG + av_log(NULL, AV_LOG_DEBUG, "mpegts_ctx->pid_cnt=%d != item_cnt=%d\n", + mpegts_ctx->pid_cnt, item_cnt); +#endif + return 0; + } + for (idx = 0; idx < item_cnt; idx++) + { + /* check for pid */ + int loc = find_in_list(mpegts_ctx->pmt_pids, items[idx].pid); + if (loc < 0) + { +#ifdef DEBUG + av_log(NULL, AV_LOG_DEBUG, + "find_in_list(..,[%d].pid=%d) => -1\n" + "is_pmt_same() => false\n", + idx, items[idx].pid); +#endif + return 0; + } + + /* check stream type */ + MpegTSFilter *tss = mpegts_ctx->pids[items[idx].pid]; + if (!tss) + { +#ifdef DEBUG + av_log(NULL, AV_LOG_DEBUG, + "mpegts_ctx->pids[items[%d].pid=%d] => null\n" + "is_pmt_same() => false\n", + idx, items[idx].pid); +#endif + return 0; + } + if (tss->type == MPEGTS_PES) + { + PESContext *pes = (PESContext*) tss->u.pes_filter.opaque; + if (!pes) + { +#ifdef DEBUG + av_log(NULL, AV_LOG_DEBUG, "pes == null, where idx %d\n" + "is_pmt_same() => false\n", idx); +#endif + return 0; + } + if (pes->stream_type != items[idx].type) + { +#ifdef DEBUG + av_log(NULL, AV_LOG_DEBUG, + "pes->stream_type != items[%d].type\n" + "is_pmt_same() => false\n", idx); +#endif + return 0; + } + } + else if (tss->type == MPEGTS_SECTION) + { + SectionContext *sect = (SectionContext*) tss->u.section_filter.opaque; + if (!sect) + { +#ifdef DEBUG + av_log(NULL, AV_LOG_DEBUG, "sect == null, where idx %d\n" + "is_pmt_same() => false\n", idx); +#endif + return 0; + } + if (sect->stream_type != items[idx].type) + { +#ifdef DEBUG + av_log(NULL, AV_LOG_DEBUG, + "sect->stream_type != items[%d].type\n" + "is_pmt_same() => false\n", idx); +#endif + return 0; + } + } + else + { +#ifdef DEBUG + av_log(NULL, AV_LOG_DEBUG, + "tss->type != MPEGTS_PES, where idx %d\n" + "is_pmt_same() => false\n", idx); +#endif + return 0; + } + } +#ifdef DEBUG + av_log(NULL, AV_LOG_DEBUG, "is_pmt_same() => true\n", idx); +#endif + return 1; +} + +static void mpegts_cleanup_streams(MpegTSContext *ts) +{ + int i; + int orig_pid_cnt = ts->pid_cnt; + for (i=0; ipid_cnt; i++) + { + if (!ts->pids[ts->pmt_pids[i]]) + { + mpegts_remove_stream(ts, ts->pmt_pids[i]); + i--; + } + } + if (orig_pid_cnt != ts->pid_cnt) + { + av_log(NULL, AV_LOG_DEBUG, + "mpegts_cleanup_streams: pid_cnt bfr %d aft %d\n", + orig_pid_cnt, ts->pid_cnt); + } +} + +static AVStream *new_section_av_stream(SectionContext *sect, enum AVMediaType type, + enum AVCodecID id) +{ + FF_ALLOCZ_OR_GOTO(NULL, sect->st, sizeof(AVStream), fail); + + sect->st = av_new_stream(sect->stream, sect->pid); + + av_set_pts_info(sect->st, 33, 1, 90000); + + sect->st->codec->codec_type = type; + sect->st->codec->codec_id = id; + sect->st->priv_data = sect; + sect->st->need_parsing = AVSTREAM_PARSE_NONE; + + return sect->st; +fail: /*for the CHECKED_ALLOCZ macro*/ + return NULL; +} + +static void mpegts_add_stream(MpegTSContext *ts, int id, pmt_entry_t* item, + uint32_t prog_reg_desc, int pcr_pid) +{ + AVStream *st = NULL; + int pid = item->pid; + + av_log(NULL, AV_LOG_DEBUG, + "mpegts_add_stream: at pid 0x%x with type %i\n", item->pid, item->type); + + if (ts->pid_cnt < PMT_PIDS_MAX) + { + if (item->type == STREAM_TYPE_DSMCC_B) + { + SectionContext *sect = NULL; + sect = add_section_stream(ts, item->pid, item->type); + if (!sect) + { + av_log(NULL, AV_LOG_ERROR, "mpegts_add_stream: " + "error creating Section context for pid 0x%x with type %i\n", + item->pid, item->type); + return; + } + + st = new_section_av_stream(sect, item->codec_type, item->codec_id); + if (!st) + { + av_log(NULL, AV_LOG_ERROR, "mpegts_add_stream: " + "error creating A/V stream for pid 0x%x with type %i\n", + item->pid, item->type); + return; + } + + st->component_tag = item->dvbci.component_tag; + st->codec->flags = item->dvbci.data_id; + st->codec->sub_id = item->dvbci.carousel_id; + + ts->pmt_pids[ts->pid_cnt] = item->pid; + ts->pid_cnt++; + + av_log(NULL, AV_LOG_DEBUG, "mpegts_add_stream: " + "stream #%d, has id 0x%x and codec %s, type %s at 0x%x\n", + st->index, st->id, ff_codec_id_string(st->codec->codec_id), + ff_codec_type_string(st->codec->codec_type), st); + } else { + PESContext *pes = NULL; + + if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) { + pes = ts->pids[pid]->u.pes_filter.opaque; + st = pes->st; + } else { + if (ts->pids[pid]) { + //wrongly added sdt filter probably + mpegts_close_filter(ts, ts->pids[pid]); + } + pes = add_pes_stream(ts, pid, pcr_pid); + if (pes) + st = av_new_stream(pes->stream, pes->pid); + else + { + av_log(NULL, AV_LOG_ERROR, "mpegts_add_stream: " + "error creating PES context for pid 0x%x with type %i\n", + item->pid, item->type); + return; + } + } + + if (!st) + { + av_log(NULL, AV_LOG_ERROR, "mpegts_add_stream: " + "error creating A/V stream for pid 0x%x with type %i\n", + item->pid, item->type); + return; + } + + if (!pes->stream_type) + mpegts_set_stream_info(st, pes, item->type, prog_reg_desc); + + st->codec->codec_tag = item->dvbci.codec_tag; + + if (prog_reg_desc == AV_RL32("HDMV") && item->type == 0x83 && pes->sub_st) { + ff_program_add_stream_index(ts->stream, id, pes->sub_st->index); + pes->sub_st->codec->codec_tag = st->codec->codec_tag; + } + + if (st->codec->codec_type != item->codec_type || + st->codec->codec_id != item->codec_id) { + st->codec->codec_type = item->codec_type; + st->codec->codec_id = item->codec_id; + } + + ts->pmt_pids[ts->pid_cnt] = item->pid; + ts->pid_cnt++; + + if (item->dvbci.language[0]) + av_dict_set(&st->metadata, "language", item->dvbci.language, 0); + + if (item->dvbci.sub_id && (item->codec_id == AV_CODEC_ID_DVB_SUBTITLE)) + st->codec->sub_id = item->dvbci.sub_id; + + st->component_tag = item->dvbci.component_tag; + + av_log(NULL, AV_LOG_DEBUG, "mpegts_add_stream: " + "stream #%d, has id 0x%x and codec %s, type %s at 0x%x\n", + st->index, st->id, ff_codec_id_string(st->codec->codec_id), + ff_codec_type_string(st->codec->codec_type), st); + } + add_pid_to_pmt(ts, id, pid); + ff_program_add_stream_index(ts->stream, id, st->index); + } + else + { + av_log(NULL, AV_LOG_ERROR, + "ERROR: adding pes stream at pid 0x%x, pid_cnt = %i\n", + item->pid, ts->pid_cnt); + } +} + +void mpegts_remove_stream(MpegTSContext *ts, int pid) +{ + av_log(NULL, AV_LOG_DEBUG, "mpegts_remove_stream 0x%x\n", pid); + if (ts->pids[pid]) + { + av_log(NULL, AV_LOG_DEBUG, "closing filter for pid 0x%x\n", pid); + mpegts_close_filter(ts, ts->pids[pid]); + } + int indx = find_in_list(ts->pmt_pids, pid); + if (indx >= 0) + { + memmove(ts->pmt_pids+indx, ts->pmt_pids+indx+1, PMT_PIDS_MAX-indx-1); + ts->pmt_pids[PMT_PIDS_MAX-1] = 0; + ts->pid_cnt--; + } + else + { + av_log(NULL, AV_LOG_DEBUG, "ERROR: closing filter for pid 0x%x, indx = %i\n", pid, indx); + } +} + +static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) +{ + MpegTSContext *ts = filter->u.section_filter.opaque; + SectionHeader h1, *h = &h1; + const uint8_t *p, *p_end; + int sid, pmt_pid; + AVProgram *program; + char buf[256]; + + int pmt_pnums[PAT_MAX_PMT]; + int pmt_pids[PAT_MAX_PMT]; + unsigned int pmt_count = 0; + int i; + + av_dlog(ts->stream, "PAT:\n"); + hex_dump_debug(ts->stream, (uint8_t *)section, section_len); + + p_end = section + section_len - 4; + p = section; + if (parse_section_header(h, &p, p_end) < 0) + return; + if (h->tid != PAT_TID) + return; + + for (i = 0; i < PAT_MAX_PMT; ++i) + { + pmt_pnums[i] = get16(&p, p_end); + if (pmt_pnums[i] < 0) + break; + + pmt_pids[i] = get16(&p, p_end) & 0x1fff; + if (pmt_pids[i] < 0) + break; + + if (pmt_pids[i] == 0x0) + { + av_log(NULL, AV_LOG_ERROR, "Invalid PAT ignored " + "MPEG Program Number=0x%x pid=0x%x req_sid=0x%x\n", + pmt_pnums[i], pmt_pids[i], ts->req_sid); + return; + } + + pmt_count++; + +#ifdef DEBUG + av_log(ts->stream, AV_LOG_DEBUG, + "MPEG Program Number=0x%x pid=0x%x req_sid=0x%x\n", + pmt_pnums[i], pmt_pids[i], ts->req_sid); +#endif + } + + if (!is_pat_same(ts, pmt_pnums, pmt_pids, pmt_count)) + { +#ifdef DEBUG + av_log(NULL, AV_LOG_DEBUG, "New PAT!\n"); +#endif + /* if there were services, get rid of them */ + ts->nb_prg = 0; + + /* if there are new services, add them */ + for (i = 0; i < pmt_count; ++i) + { + snprintf(buf, sizeof(buf), "MPEG Program %x", pmt_pnums[i]); + add_pat_entry(ts, pmt_pnums[i], pmt_pids[i]); + } + } + + int found = 0; + for (i = 0; i < pmt_count; ++i) + { + /* if an MPEG program number is requested, and this is that program, + * add a filter for the PMT. */ + if (ts->req_sid == pmt_pnums[i]) + { +#ifdef DEBUG + av_log(NULL, AV_LOG_DEBUG, "Found program number!\n"); +#endif + /* close old filter if it doesn't match */ + if (ts->pmt_filter) + { + MpegTSFilter *f = ts->pmt_filter; + MpegTSSectionFilter *sec = &f->u.section_filter; + + if ((f->pid != pmt_pids[i]) || + (f->type != MPEGTS_SECTION) || + (sec->section_cb != pmt_cb) || + (sec->opaque != ts)) + { + mpegts_close_filter(ts, ts->pmt_filter); + ts->pmt_filter = NULL; + } + } + + /* create new pmt_filter if we need one */ + if (!ts->pmt_filter) + { + ts->pmt_filter = mpegts_open_section_filter( + ts, pmt_pids[i], pmt_cb, ts, 1); + } + + found = 1; + } + } + + /* if we are scanning for any PAT and not a particular PMT, + * tell parser it is safe to quit. */ + if (ts->req_sid < 0 && ts->scanning) + { +#ifdef DEBUG + av_log(NULL, AV_LOG_DEBUG, "Found PAT, ending scan\n"); +#endif + ts->stop_parse = 1; + } + + /* if we are looking for a particular MPEG program number, + * and it is not in this PAT indicate this in "pmt_scan_state" + * and tell parser it is safe to quit. */ + if (ts->req_sid >= 0 && !found) + { +#ifdef DEBUG + av_log(NULL, AV_LOG_DEBUG, "Program 0x%x is not in PAT, ending scan\n", + ts->req_sid); +#endif + ts->pmt_scan_state = PMT_NOT_IN_PAT; + ts->stop_parse = 1; + } +} + +static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) +{ + MpegTSContext *ts = filter->u.section_filter.opaque; + SectionHeader h1, *h = &h1; + const uint8_t *p, *p_end, *desc_list_end, *desc_end; + int onid, val, sid, desc_list_len, desc_tag, desc_len, service_type; + char *name, *provider_name; + + av_dlog(ts->stream, "SDT:\n"); + hex_dump_debug(ts->stream, (uint8_t *)section, section_len); + + p_end = section + section_len - 4; + p = section; + if (parse_section_header(h, &p, p_end) < 0) + return; + if (h->tid != SDT_TID) + return; + onid = get16(&p, p_end); + if (onid < 0) + return; + val = get8(&p, p_end); + if (val < 0) + return; + for(;;) { + sid = get16(&p, p_end); + if (sid < 0) + break; + val = get8(&p, p_end); + if (val < 0) + break; + desc_list_len = get16(&p, p_end); + if (desc_list_len < 0) + break; + desc_list_len &= 0xfff; + desc_list_end = p + desc_list_len; + if (desc_list_end > p_end) + break; + for(;;) { + desc_tag = get8(&p, desc_list_end); + if (desc_tag < 0) + break; + desc_len = get8(&p, desc_list_end); + desc_end = p + desc_len; + if (desc_end > desc_list_end) + break; + + av_dlog(ts->stream, "tag: 0x%02x len=%d\n", + desc_tag, desc_len); + + switch(desc_tag) { + case 0x48: + service_type = get8(&p, p_end); + if (service_type < 0) + break; + provider_name = getstr8(&p, p_end); + if (!provider_name) + break; + name = getstr8(&p, p_end); + if (name) { + AVProgram *program = av_new_program(ts->stream, sid); + if(program) { + av_dict_set(&program->metadata, "service_name", name, 0); + av_dict_set(&program->metadata, "service_provider", provider_name, 0); + } + } + av_free(name); + av_free(provider_name); + break; + default: + break; + } + p = desc_end; + } + p = desc_list_end; + } +} + +static SectionContext *add_section_stream(MpegTSContext *ts, int pid, int stream_type) +{ + MpegTSFilter *tss = ts->pids[pid]; + SectionContext *sect = 0; + if (tss) { /* filter already exists */ + if (tss->type == MPEGTS_SECTION) + sect = (SectionContext*) tss->u.section_filter.opaque; + + if (sect && (sect->stream_type == stream_type)) + return sect; /* if it's the same stream type, just return ok */ + + /* otherwise, kill it, and start a new stream */ + mpegts_close_filter(ts, tss); + } + + /* create a SECTION context */ + if (!(sect=av_mallocz(sizeof(SectionContext)))) { + av_log(NULL, AV_LOG_ERROR, "Error: av_mallocz() failed in add_section_stream"); + return 0; + } + sect->ts = ts; + sect->stream = ts->stream; + sect->pid = pid; + sect->stream_type = stream_type; + tss = mpegts_open_section_filter(ts, pid, mpegts_push_section, sect, 1); + if (!tss) { + av_free(sect); + av_log(NULL, AV_LOG_ERROR, "Error: unable to open mpegts Section filter in add_section_stream"); + return 0; + } + + return sect; +} + +/* handle one TS packet */ +static int handle_packet(MpegTSContext *ts, const uint8_t *packet) +{ + AVFormatContext *s = ts->stream; + MpegTSFilter *tss; + int len, pid, cc, expected_cc, cc_ok, afc, is_start, is_discontinuity, + has_adaptation, has_payload; + const uint8_t *p, *p_end; + int64_t pos; + + pid = AV_RB16(packet + 1) & 0x1fff; + + if (!ts->pids[0]) { + /* make sure we're always scanning for new PAT's */ + av_log(ts->stream, AV_LOG_INFO, "opening pat filter\n"); + ts->pat_filter = mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1); + } + + if(pid && discard_pid(ts, pid)) + { + av_log(ts->stream, AV_LOG_INFO, "discarding pid %d\n", pid); + return 0; + } + + is_start = packet[1] & 0x40; + tss = ts->pids[pid]; + if (ts->auto_guess && tss == NULL && is_start) { + add_pes_stream(ts, pid, -1); + tss = ts->pids[pid]; + } + if (!tss) + return 0; + + afc = (packet[3] >> 4) & 3; + if (afc == 0) /* reserved value */ + return 0; + has_adaptation = afc & 2; + has_payload = afc & 1; + is_discontinuity = has_adaptation + && packet[4] != 0 /* with length > 0 */ + && (packet[5] & 0x80); /* and discontinuity indicated */ + + /* continuity check (currently not used) */ + cc = (packet[3] & 0xf); + expected_cc = has_payload ? (tss->last_cc + 1) & 0x0f : tss->last_cc; + cc_ok = pid == 0x1FFF // null packet PID + || is_discontinuity + || tss->last_cc < 0 + || expected_cc == cc; + + tss->last_cc = cc; + if (!cc_ok) { + av_log(ts->stream, AV_LOG_DEBUG, + "Continuity check failed for pid %d expected %d got %d\n", + pid, expected_cc, cc); + if(tss->type == MPEGTS_PES) { + PESContext *pc = tss->u.pes_filter.opaque; + pc->flags |= AV_PKT_FLAG_CORRUPT; + } + } + + if (!has_payload) + return 0; + p = packet + 4; + if (has_adaptation) { + /* skip adaptation field */ + p += p[0] + 1; + } + /* if past the end of packet, ignore */ + p_end = packet + TS_PACKET_SIZE; + if (p >= p_end) + return 0; + + pos = avio_tell(ts->stream->pb); + ts->pos47= pos % ts->raw_packet_size; + + if (tss->type == MPEGTS_SECTION) { + if (is_start) { + /* pointer field present */ + len = *p++; + if (p + len > p_end) + { + av_log(s, AV_LOG_WARNING, "handle_packet: Last section data too long on PID=%#x, %d\n", pid, cc); + return 0; + } + if (len && cc_ok) { + /* write remaining section bytes */ + write_section_data(s, tss, + p, len, 0); + /* check whether filter has been closed */ + if (!ts->pids[pid]) + return 0; + } + p += len; + if (p < p_end) { + write_section_data(s, tss, + p, p_end - p, 1); + } + } else { + if (cc_ok) { + write_section_data(s, tss, + p, p_end - p, 0); + } + } + } else { + int ret; + // Note: The position here points actually behind the current packet. + if ((ret = tss->u.pes_filter.pes_cb(tss, p, p_end - p, is_start, + pos - ts->raw_packet_size)) < 0) + return ret; + } + + return 0; +} + +/* XXX: try to find a better synchro over several packets (use + get_packet_size() ?) */ +static int mpegts_resync(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + int c, i; + + for(i = 0;i < MAX_RESYNC_SIZE; i++) { + c = avio_r8(pb); + if (url_feof(pb)) + return -1; + if (c == 0x47) { + avio_seek(pb, -1, SEEK_CUR); + return 0; + } + } + av_log(s, AV_LOG_ERROR, "max resync size reached, could not find sync byte\n"); + /* no sync found */ + return -1; +} + +/* return -1 if error or EOF. Return 0 if OK. */ +static int read_packet(AVFormatContext *s, uint8_t *buf, int raw_packet_size) +{ + AVIOContext *pb = s->pb; + int skip, len; + + for(;;) { + len = avio_read(pb, buf, TS_PACKET_SIZE); + if (len != TS_PACKET_SIZE) + return len < 0 ? len : AVERROR_EOF; + /* check packet sync byte */ + if (buf[0] != 0x47) { + /* find a new packet start */ + avio_seek(pb, -TS_PACKET_SIZE, SEEK_CUR); + if (mpegts_resync(s) < 0) + return AVERROR(EAGAIN); + else + continue; + } else { + skip = raw_packet_size - TS_PACKET_SIZE; + if (skip > 0) + avio_skip(pb, skip); + break; + } + } + return 0; +} + +static int handle_packets(MpegTSContext *ts, int nb_packets) +{ + AVFormatContext *s = ts->stream; + uint8_t packet[TS_PACKET_SIZE + FF_INPUT_BUFFER_PADDING_SIZE]; + int packet_num, ret = 0; + + if (avio_tell(s->pb) != ts->last_pos) { + int i; + av_dlog(ts->stream, "Skipping after seek\n"); + /* seek detected, flush pes buffer */ + for (i = 0; i < NB_PID_MAX; i++) { + if (ts->pids[i]) { + if (ts->pids[i]->type == MPEGTS_PES) { + PESContext *pes = ts->pids[i]->u.pes_filter.opaque; + av_freep(&pes->buffer); + pes->data_index = 0; + pes->state = MPEGTS_SKIP; /* skip until pes header */ + } + ts->pids[i]->last_cc = -1; + } + } + } + + ts->stop_parse = 0; + packet_num = 0; + memset(packet + TS_PACKET_SIZE, 0, FF_INPUT_BUFFER_PADDING_SIZE); + for(;;) { + packet_num++; + if (nb_packets != 0 && packet_num >= nb_packets || + ts->stop_parse > 1) { + ret = AVERROR(EAGAIN); + break; + } + if (ts->stop_parse > 0) + break; + + ret = read_packet(s, packet, ts->raw_packet_size); + if (ret != 0) + break; + ret = handle_packet(ts, packet); + if (ret != 0) + break; + } + ts->last_pos = avio_tell(s->pb); + return ret; +} + +static int mpegts_probe(AVProbeData *p) +{ + const int size= p->buf_size; + int maxscore=0; + int sumscore=0; + int i; + int check_count= size / TS_FEC_PACKET_SIZE; +#define CHECK_COUNT 10 +#define CHECK_BLOCK 100 + + if (check_count < CHECK_COUNT) + return -1; + + for (i=0; ibuf + TS_PACKET_SIZE *i, TS_PACKET_SIZE *left, TS_PACKET_SIZE , NULL); + int dvhs_score= analyze(p->buf + TS_DVHS_PACKET_SIZE*i, TS_DVHS_PACKET_SIZE*left, TS_DVHS_PACKET_SIZE, NULL); + int fec_score = analyze(p->buf + TS_FEC_PACKET_SIZE *i, TS_FEC_PACKET_SIZE *left, TS_FEC_PACKET_SIZE , NULL); + score = FFMAX3(score, dvhs_score, fec_score); + sumscore += score; + maxscore = FFMAX(maxscore, score); + } + + sumscore = sumscore*CHECK_COUNT/check_count; + maxscore = maxscore*CHECK_COUNT/CHECK_BLOCK; + + av_dlog(0, "TS score: %d %d\n", sumscore, maxscore); + + if (sumscore > 6) return AVPROBE_SCORE_MAX + sumscore - CHECK_COUNT; + else if (maxscore > 6) return AVPROBE_SCORE_MAX/2 + sumscore - CHECK_COUNT; + else return -1; +} + +/* return the 90kHz PCR and the extension for the 27MHz PCR. return + (-1) if not available */ +static int parse_pcr(int64_t *ppcr_high, int *ppcr_low, + const uint8_t *packet) +{ + int afc, len, flags; + const uint8_t *p; + unsigned int v; + + afc = (packet[3] >> 4) & 3; + if (afc <= 1) + return -1; + p = packet + 4; + len = p[0]; + p++; + if (len == 0) + return -1; + flags = *p++; + len--; + if (!(flags & 0x10)) + return -1; + if (len < 6) + return -1; + v = AV_RB32(p); + *ppcr_high = ((int64_t)v << 1) | (p[4] >> 7); + *ppcr_low = ((p[4] & 1) << 8) | p[5]; + return 0; +} + +static int mpegts_read_header(AVFormatContext *s) +{ + MpegTSContext *ts = s->priv_data; + AVIOContext *pb = s->pb; + uint8_t buf[8*1024] = {0}; + int len, sid, i; + int64_t pos; + + memset(ts->pids, 0, NB_PID_MAX * sizeof(MpegTSFilter *)); + + /* read the first 8192 bytes to get packet size */ + pos = avio_tell(pb); + len = avio_read(pb, buf, sizeof(buf)); + ts->raw_packet_size = get_packet_size(buf, len); + av_log(NULL, AV_LOG_DEBUG, "mpegts_read_header: TS packet size = %d\n", + ts->raw_packet_size); + if (ts->raw_packet_size <= 0) { + av_log(s, AV_LOG_WARNING, "Could not detect TS packet size, defaulting to non-FEC/DVHS\n"); + ts->raw_packet_size = TS_PACKET_SIZE; + } + ts->stream = s; + ts->auto_guess = 0; + + if (s->iformat == &ff_mythtv_mpegts_demuxer) { + /* normal demux */ + + if (!ts->auto_guess) { + /* first do a scan to get all the services */ + /* NOTE: We attempt to seek on non-seekable files as well, as the + * probe buffer usually is big enough. Only warn if the seek failed + * on files where the seek should work. */ + if (avio_seek(pb, pos, SEEK_SET) < 0) + av_log(s, pb->seekable ? AV_LOG_ERROR : AV_LOG_INFO, "Unable to seek back to the start\n"); + + /* SDT Scan Removed here. It caused startup delays in TS files + SDT will not exist in a stripped TS file created by myth. */ +#if 0 + mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1); +#endif + + /* we don't want any PMT pid filters created on first pass */ + ts->req_sid = -1; + + ts->scanning = 1; + ts->pat_filter = + mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1); + + handle_packets(ts, s->probesize / ts->raw_packet_size); + ts->scanning = 0; + + if (ts->nb_prg <= 0) { + /* Guess this is a raw transport stream with no PAT tables. */ + ts->auto_guess = 1; + s->ctx_flags |= AVFMTCTX_NOHEADER; + goto do_pcr; + } + + ts->scanning = 1; + ts->pmt_scan_state = PMT_NOT_YET_FOUND; + /* tune to first service found */ + for (i = 0; ((i < ts->nb_prg) && + (ts->pmt_scan_state == PMT_NOT_YET_FOUND)); i++) + { +#ifdef DEBUG + av_log(ts->stream, AV_LOG_DEBUG, "Tuning to pnum: 0x%x\n", + ts->prg[i].id); +#endif + + /* now find the info for the first service if we found any, + otherwise try to filter all PATs */ + + avio_seek(pb, pos, SEEK_SET); + ts->req_sid = sid = ts->prg[i].id; + handle_packets(ts, s->probesize / ts->raw_packet_size); + + /* fallback code to deal with broken streams from + * DBOX2/Firewire cable boxes. */ + if (ts->pmt_filter && + (ts->pmt_scan_state == PMT_NOT_YET_FOUND)) + { + av_log(NULL, AV_LOG_ERROR, + "Tuning to pnum: 0x%x without CRC check on PMT\n", + ts->prg[i].id); + /* turn off crc checking */ + ts->pmt_filter->u.section_filter.check_crc = 0; + /* try again */ + avio_seek(pb, pos, SEEK_SET); + ts->req_sid = sid = ts->prg[i].id; + handle_packets(ts, s->probesize / ts->raw_packet_size); + } + + /* fallback code to deal with streams that are not complete PMT + * streams (BBC iPlayer IPTV as an example) */ + if (ts->pmt_filter && + (ts->pmt_scan_state == PMT_NOT_YET_FOUND)) + { + av_log(NULL, AV_LOG_ERROR, + "Overriding PMT data length, using " + "contents of first TS packet only!\n"); + ts->pmt_filter->pmt_chop_at_ts = 1; + /* try again */ + avio_seek(pb, pos, SEEK_SET); + ts->req_sid = sid = ts->prg[i].id; + handle_packets(ts, s->probesize / ts->raw_packet_size); + } + } + ts->scanning = 0; + + /* if we could not find any PMTs, fail */ + if (ts->pmt_scan_state == PMT_NOT_YET_FOUND) + { + av_log(NULL, AV_LOG_ERROR, + "mpegts_read_header: could not find any PMT's\n"); + goto fail; + } + av_dlog(ts->stream, "tuning done\n"); + } + + s->ctx_flags |= AVFMTCTX_NOHEADER; + } else { + AVStream *st; + int pcr_pid, pid, nb_packets, nb_pcrs, ret, pcr_l; + int64_t pcrs[2], pcr_h; + int packet_count[2]; + uint8_t packet[TS_PACKET_SIZE]; + + /* only read packets */ + + do_pcr: + st = avformat_new_stream(s, NULL); + if (!st) + { + av_log(NULL, AV_LOG_ERROR, "mpegts_read_header: " + "av_new_stream() failed\n"); + goto fail; + } + avpriv_set_pts_info(st, 60, 1, 27000000); + st->codec->codec_type = AVMEDIA_TYPE_DATA; + st->codec->codec_id = AV_CODEC_ID_MPEG2TS; + + /* we iterate until we find two PCRs to estimate the bitrate */ + pcr_pid = -1; + nb_pcrs = 0; + nb_packets = 0; + for(;;) { + ret = read_packet(s, packet, ts->raw_packet_size); + if (ret < 0) + { + av_log(NULL, AV_LOG_ERROR, "mpegts_read_header: " + "read_packet() failed\n"); + return -1; + } + pid = AV_RB16(packet + 1) & 0x1fff; + if ((pcr_pid == -1 || pcr_pid == pid) && + parse_pcr(&pcr_h, &pcr_l, packet) == 0) { + pcr_pid = pid; + packet_count[nb_pcrs] = nb_packets; + pcrs[nb_pcrs] = pcr_h * 300 + pcr_l; + nb_pcrs++; + if (nb_pcrs >= 2) + break; + } + nb_packets++; + } + + /* NOTE1: the bitrate is computed without the FEC */ + /* NOTE2: it is only the bitrate of the start of the stream */ + ts->pcr_incr = (pcrs[1] - pcrs[0]) / (packet_count[1] - packet_count[0]); + ts->cur_pcr = pcrs[0] - ts->pcr_incr * packet_count[0]; + s->bit_rate = (TS_PACKET_SIZE * 8) * 27e6 / ts->pcr_incr; + st->codec->bit_rate = s->bit_rate; + st->start_time = ts->cur_pcr; + av_dlog(ts->stream, "start=%0.3f pcr=%0.3f incr=%d\n", + st->start_time / 1000000.0, pcrs[0] / 27e6, ts->pcr_incr); + } + + avio_seek(pb, pos, SEEK_SET); + return 0; + fail: + return -1; +} + +#define MAX_PACKET_READAHEAD ((128 * 1024) / 188) + +static int mpegts_raw_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + MpegTSContext *ts = s->priv_data; + int ret, i; + int64_t pcr_h, next_pcr_h, pos; + int pcr_l, next_pcr_l; + uint8_t pcr_buf[12]; + + if (av_new_packet(pkt, TS_PACKET_SIZE) < 0) + return AVERROR(ENOMEM); + pkt->pos= avio_tell(s->pb); + ret = read_packet(s, pkt->data, ts->raw_packet_size); + if (ret < 0) { + av_free_packet(pkt); + return ret; + } + if (ts->mpeg2ts_compute_pcr) { + /* compute exact PCR for each packet */ + if (parse_pcr(&pcr_h, &pcr_l, pkt->data) == 0) { + /* we read the next PCR (XXX: optimize it by using a bigger buffer */ + pos = avio_tell(s->pb); + for(i = 0; i < MAX_PACKET_READAHEAD; i++) { + avio_seek(s->pb, pos + i * ts->raw_packet_size, SEEK_SET); + avio_read(s->pb, pcr_buf, 12); + if (parse_pcr(&next_pcr_h, &next_pcr_l, pcr_buf) == 0) { + /* XXX: not precise enough */ + ts->pcr_incr = ((next_pcr_h - pcr_h) * 300 + (next_pcr_l - pcr_l)) / + (i + 1); + break; + } + } + avio_seek(s->pb, pos, SEEK_SET); + /* no next PCR found: we use previous increment */ + ts->cur_pcr = pcr_h * 300 + pcr_l; + } + pkt->pts = ts->cur_pcr; + pkt->duration = ts->pcr_incr; + ts->cur_pcr += ts->pcr_incr; + } + pkt->stream_index = 0; + return 0; +} + +static int mpegts_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + MpegTSContext *ts = s->priv_data; + int ret, i; + + ts->pkt = pkt; + ret = handle_packets(ts, 0); + if (ret < 0) { + av_free_packet(ts->pkt); + /* flush pes data left */ + for (i = 0; i < NB_PID_MAX; i++) { + if (ts->pids[i] && ts->pids[i]->type == MPEGTS_PES) { + PESContext *pes = ts->pids[i]->u.pes_filter.opaque; + if (pes->state == MPEGTS_PAYLOAD && pes->data_index > 0) { + new_pes_packet(pes, pkt); + pes->state = MPEGTS_SKIP; + ret = 0; + break; + } + } + } + } + + return ret; +} + +static int mpegts_read_close(AVFormatContext *s) +{ + MpegTSContext *ts = s->priv_data; + int i; + + clear_programs(ts); + + for(i=0;ipids[i]) mpegts_close_filter(ts, ts->pids[i]); + + return 0; +} + +static int64_t mpegts_get_pcr(AVFormatContext *s, int stream_index, + int64_t *ppos, int64_t pos_limit) +{ + MpegTSContext *ts = s->priv_data; + int64_t pos, timestamp; + uint8_t buf[TS_PACKET_SIZE]; + int pcr_l, pcr_pid = ((PESContext*)s->streams[stream_index]->priv_data)->pcr_pid; + pos = ((*ppos + ts->raw_packet_size - 1 - ts->pos47) / ts->raw_packet_size) * ts->raw_packet_size + ts->pos47; + while(pos < pos_limit) { + if (avio_seek(s->pb, pos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + if (avio_read(s->pb, buf, TS_PACKET_SIZE) != TS_PACKET_SIZE) + return AV_NOPTS_VALUE; + if (buf[0] != 0x47) { + if (mpegts_resync(s) < 0) + return AV_NOPTS_VALUE; + pos = avio_tell(s->pb); + continue; + } + if ((pcr_pid < 0 || (AV_RB16(buf + 1) & 0x1fff) == pcr_pid) && + parse_pcr(×tamp, &pcr_l, buf) == 0) { + *ppos = pos; + return timestamp; + } + pos += ts->raw_packet_size; + } + + return AV_NOPTS_VALUE; +} + +static int64_t mpegts_get_dts(AVFormatContext *s, int stream_index, + int64_t *ppos, int64_t pos_limit) +{ + MpegTSContext *ts = s->priv_data; + int64_t pos; + pos = ((*ppos + ts->raw_packet_size - 1 - ts->pos47) / ts->raw_packet_size) * ts->raw_packet_size + ts->pos47; + ff_read_frame_flush(s); + if (avio_seek(s->pb, pos, SEEK_SET) < 0) + return AV_NOPTS_VALUE; + while(pos < pos_limit) { + int ret; + AVPacket pkt; + av_init_packet(&pkt); + ret= av_read_frame(s, &pkt); + if(ret < 0) + return AV_NOPTS_VALUE; + av_free_packet(&pkt); + if(pkt.dts != AV_NOPTS_VALUE && pkt.pos >= 0){ + ff_reduce_index(s, pkt.stream_index); + av_add_index_entry(s->streams[pkt.stream_index], pkt.pos, pkt.dts, 0, 0, AVINDEX_KEYFRAME /* FIXME keyframe? */); + if(pkt.stream_index == stream_index){ + *ppos= pkt.pos; + return pkt.dts; + } + } + pos = pkt.pos; + } + + return AV_NOPTS_VALUE; +} + +#ifdef USE_SYNCPOINT_SEARCH + +static int read_seek2(AVFormatContext *s, + int stream_index, + int64_t min_ts, + int64_t target_ts, + int64_t max_ts, + int flags) +{ + int64_t pos; + + int64_t ts_ret, ts_adj; + int stream_index_gen_search; + AVStream *st; + AVParserState *backup; + + backup = ff_store_parser_state(s); + + // detect direction of seeking for search purposes + flags |= (target_ts - min_ts > (uint64_t)(max_ts - target_ts)) ? + AVSEEK_FLAG_BACKWARD : 0; + + if (flags & AVSEEK_FLAG_BYTE) { + // use position directly, we will search starting from it + pos = target_ts; + } else { + // search for some position with good timestamp match + if (stream_index < 0) { + stream_index_gen_search = av_find_default_stream_index(s); + if (stream_index_gen_search < 0) { + ff_restore_parser_state(s, backup); + return -1; + } + + st = s->streams[stream_index_gen_search]; + // timestamp for default must be expressed in AV_TIME_BASE units + ts_adj = av_rescale(target_ts, + st->time_base.den, + AV_TIME_BASE * (int64_t)st->time_base.num); + } else { + ts_adj = target_ts; + stream_index_gen_search = stream_index; + } + pos = ff_gen_search(s, stream_index_gen_search, ts_adj, + 0, INT64_MAX, -1, + AV_NOPTS_VALUE, + AV_NOPTS_VALUE, + flags, &ts_ret, mpegts_get_pcr); + if (pos < 0) { + ff_restore_parser_state(s, backup); + return -1; + } + } + + // search for actual matching keyframe/starting position for all streams + if (ff_gen_syncpoint_search(s, stream_index, pos, + min_ts, target_ts, max_ts, + flags) < 0) { + ff_restore_parser_state(s, backup); + return -1; + } + + ff_free_parser_state(s, backup); + return 0; +} + +static int read_seek(AVFormatContext *s, int stream_index, int64_t target_ts, int flags) +{ + int ret; + if (flags & AVSEEK_FLAG_BACKWARD) { + flags &= ~AVSEEK_FLAG_BACKWARD; + ret = read_seek2(s, stream_index, INT64_MIN, target_ts, target_ts, flags); + if (ret < 0) + // for compatibility reasons, seek to the best-fitting timestamp + ret = read_seek2(s, stream_index, INT64_MIN, target_ts, INT64_MAX, flags); + } else { + ret = read_seek2(s, stream_index, target_ts, target_ts, INT64_MAX, flags); + if (ret < 0) + // for compatibility reasons, seek to the best-fitting timestamp + ret = read_seek2(s, stream_index, INT64_MIN, target_ts, INT64_MAX, flags); + } + return ret; +} + +#endif + +/**************************************************************/ +/* parsing functions - called from other demuxers such as RTP */ + +MpegTSContext *ff_mpegts_parse_open(AVFormatContext *s) +{ + MpegTSContext *ts; + + ts = av_mallocz(sizeof(MpegTSContext)); + if (!ts) + return NULL; + /* no stream case, currently used by RTP */ + ts->raw_packet_size = TS_PACKET_SIZE; + ts->stream = s; + ts->auto_guess = 1; + mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1); + mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1); + + return ts; +} + +/* return the consumed length if a packet was output, or -1 if no + packet is output */ +int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, + const uint8_t *buf, int len) +{ + int len1; + + len1 = len; + ts->pkt = pkt; + for(;;) { + ts->stop_parse = 0; + if (len < TS_PACKET_SIZE) + return -1; + if (buf[0] != 0x47) { + buf++; + len--; + } else { + handle_packet(ts, buf); + buf += TS_PACKET_SIZE; + len -= TS_PACKET_SIZE; + if (ts->stop_parse == 1) + break; + } + } + return len1 - len; +} + +void ff_mpegts_parse_close(MpegTSContext *ts) +{ + int i; + + for(i=0;ipids[i]); + av_free(ts); +} + +AVInputFormat ff_mythtv_mpegts_demuxer = { + .name = "mpegts", + .long_name = NULL_IF_CONFIG_SMALL("MPEG-2 transport stream format"), + .priv_data_size = sizeof(MpegTSContext), + .read_probe = mpegts_probe, + .read_header = mpegts_read_header, + .read_packet = mpegts_read_packet, + .read_close = mpegts_read_close, + .read_timestamp = mpegts_get_dts, + .flags = AVFMT_SHOW_IDS|AVFMT_TS_DISCONT, +#ifdef USE_SYNCPOINT_SEARCH + .read_seek2 = read_seek2, +#endif +}; + +AVInputFormat ff_mythtv_mpegtsraw_demuxer = { + .name = "mpegtsraw", + .long_name = NULL_IF_CONFIG_SMALL("MPEG-2 raw transport stream format"), + .priv_data_size = sizeof(MpegTSContext), + .read_header = mpegts_read_header, + .read_packet = mpegts_raw_read_packet, + .read_close = mpegts_read_close, + .read_timestamp = mpegts_get_dts, + .flags = AVFMT_SHOW_IDS|AVFMT_TS_DISCONT, +#ifdef USE_SYNCPOINT_SEARCH + .read_seek2 = read_seek2, +#endif + .priv_class = &mpegtsraw_class, +}; diff -ruN ./libavformat/mpegts-mythtv.h /tmp/mythtv/mythtv/external/FFmpeg/libavformat/mpegts-mythtv.h --- ./libavformat/mpegts-mythtv.h 1970-01-01 01:00:00.000000000 +0100 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavformat/mpegts-mythtv.h 2014-08-09 07:18:01.770293197 +0200 @@ -0,0 +1,148 @@ +/* + * MPEG2 transport stream defines + * Copyright (c) 2003 Fabrice Bellard + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVFORMAT_MPEGTS_MYTHTV_H +#define AVFORMAT_MPEGTS_MYTHTV_H + +#include "avformat.h" + +#define TS_FEC_PACKET_SIZE 204 +#define TS_DVHS_PACKET_SIZE 192 +#define TS_PACKET_SIZE 188 +#define TS_MAX_PACKET_SIZE 204 + +#define NB_PID_MAX 8192 +#define MAX_SECTION_SIZE 4096 + +/* pids */ +#define PAT_PID 0x0000 +#define SDT_PID 0x0011 + +/* table ids */ +#define PAT_TID 0x00 +#define PMT_TID 0x02 +#define M4OD_TID 0x05 +#define SDT_TID 0x42 + +#define DVB_CAROUSEL_ID 0x13 +#define DVB_VBI_DATA_ID 0x45 +#define DVB_VBI_TELETEXT_ID 0x46 +#define DVB_TELETEXT_ID 0x56 +#define DVB_SUBT_DESCID 0x59 +#define DVB_BROADCAST_ID 0x66 +#define DVB_DATA_STREAM 0x52 +#define STREAM_TYPE_VIDEO_MPEG1 0x01 +#define STREAM_TYPE_VIDEO_MPEG2 0x02 +#define STREAM_TYPE_AUDIO_MPEG1 0x03 +#define STREAM_TYPE_AUDIO_MPEG2 0x04 +#define STREAM_TYPE_PRIVATE_SECTION 0x05 +#define STREAM_TYPE_PRIVATE_DATA 0x06 +#define STREAM_TYPE_DSMCC_B 0x0b +#define STREAM_TYPE_AUDIO_AAC 0x0f +#define STREAM_TYPE_AUDIO_AAC_LATM 0x11 +#define STREAM_TYPE_VIDEO_MPEG4 0x10 +#define STREAM_TYPE_VIDEO_H264 0x1b +#define STREAM_TYPE_VIDEO_VC1 0xea +#define STREAM_TYPE_VIDEO_DIRAC 0xd1 + +#define STREAM_TYPE_AUDIO_AC3 0x81 +#define STREAM_TYPE_AUDIO_DTS 0x8a +#define STREAM_TYPE_AUDIO_HDMV_AC3_PLUS 0x84 +#define STREAM_TYPE_AUDIO_HDMV_AC3_TRUE_HD 0x83 +#define STREAM_TYPE_AUDIO_HDMV_DTS 0x82 +#define STREAM_TYPE_AUDIO_HDMV_DTS_HD 0x85 +#define STREAM_TYPE_AUDIO_HDMV_DTS_HD_MASTER 0x86 + +#define STREAM_TYPE_SUBTITLE_DVB 0x100 +#define STREAM_TYPE_VBI_DVB 0x101 + +typedef struct MpegTSContext MpegTSContext; + +void mpegts_remove_stream(MpegTSContext *ts, int pid); +MpegTSContext *ff_mpegts_parse_open(AVFormatContext *s); +int ff_mpegts_parse_packet(MpegTSContext *ts, AVPacket *pkt, + const uint8_t *buf, int len); +void ff_mpegts_parse_close(MpegTSContext *ts); + +typedef struct { + int use_au_start; + int use_au_end; + int use_rand_acc_pt; + int use_padding; + int use_timestamps; + int use_idle; + int timestamp_res; + int timestamp_len; + int ocr_len; + int au_len; + int inst_bitrate_len; + int degr_prior_len; + int au_seq_num_len; + int packet_seq_num_len; +} SLConfigDescr; + +typedef struct { + int es_id; + int dec_config_descr_len; + uint8_t *dec_config_descr; + SLConfigDescr sl; +} Mp4Descr; + +typedef struct +{ + char language[4]; + int comp_page; + int anc_page; + int sub_id; + int txt_type; + int vbi_data; + /* DSMCC data */ + int data_id; + int carousel_id; + int component_tag; + unsigned int codec_tag; +} dvb_caption_info_t; + +typedef struct +{ + int pid; + int type; + enum AVCodecID codec_id; + enum AVMediaType codec_type; + dvb_caption_info_t dvbci; +} pmt_entry_t; + + +/** + * Parse an MPEG-2 descriptor + * @param[in] fc Format context (used for logging only) + * @param st Stream + * @param stream_type STREAM_TYPE_xxx + * @param pp Descriptor buffer pointer + * @param desc_list_end End of buffer + * @return <0 to stop processing + */ +int ff_parse_mpeg2_descriptor(AVFormatContext *fc, pmt_entry_t *item, int stream_type, + const uint8_t **pp, const uint8_t *desc_list_end, + Mp4Descr *mp4_descr, int mp4_descr_count, int pid, + MpegTSContext *ts, dvb_caption_info_t *dvbci); + +#endif /* AVFORMAT_MPEGTS_MYTHTV_H */ diff -ruN ./libavformat/rtpdec_mpegts.c /tmp/mythtv/mythtv/external/FFmpeg/libavformat/rtpdec_mpegts.c --- ./libavformat/rtpdec_mpegts.c 2014-08-09 07:27:10.278281322 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavformat/rtpdec_mpegts.c 2014-08-09 07:18:01.786293197 +0200 @@ -20,7 +20,7 @@ */ #include "libavutil/attributes.h" -#include "mpegts.h" +#include "mpegts-mythtv.h" #include "rtpdec_formats.h" struct PayloadContext { diff -ruN ./libavformat/rtsp.c /tmp/mythtv/mythtv/external/FFmpeg/libavformat/rtsp.c --- ./libavformat/rtsp.c 2014-08-09 07:27:10.270281323 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavformat/rtsp.c 2014-08-09 07:18:01.786293197 +0200 @@ -48,7 +48,7 @@ #include "rtpenc_chain.h" #include "url.h" #include "rtpenc.h" -#include "mpegts.h" +#include "mpegts-mythtv.h" /* Timeout values for socket poll, in ms, * and read_packet(), in seconds */ diff -ruN ./libavformat/utils.c /tmp/mythtv/mythtv/external/FFmpeg/libavformat/utils.c --- ./libavformat/utils.c 2014-08-09 07:27:10.274281323 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavformat/utils.c 2014-08-09 07:18:01.798293197 +0200 @@ -53,6 +53,13 @@ #include "riff.h" #include "url.h" +/* MYTHTV CHANGES */ +extern AVInputFormat ff_mythtv_mpegts_demuxer; +extern AVInputFormat ff_mythtv_mpegtsraw_demuxer; +extern AVInputFormat ff_mpegts_demuxer; +extern AVInputFormat ff_mpegtsraw_demuxer; +/* END MYTHTV CHANGES */ + /** * @file * various utility functions for use within FFmpeg @@ -261,8 +268,19 @@ if (score > score_max) { score_max = score; fmt = fmt1; - } else if (score == score_max) - fmt = NULL; + } else if (score == score_max) { + // if the conflict is between Myth MPEGTS demux and FFMPEG's original one + // use mythtv's one + if ((fmt1 == &ff_mpegts_demuxer && fmt == &ff_mythtv_mpegts_demuxer) || + (fmt == &ff_mpegts_demuxer && fmt1 == &ff_mythtv_mpegts_demuxer)) { + fmt = &ff_mythtv_mpegts_demuxer; + } else if ((fmt1 == &ff_mpegts_demuxer && fmt == &ff_mythtv_mpegtsraw_demuxer) || + (fmt == &ff_mpegts_demuxer && fmt1 == &ff_mythtv_mpegtsraw_demuxer)) { + fmt = &ff_mythtv_mpegtsraw_demuxer; + } else { + fmt = NULL; + } + } } if (nodat) score_max = FFMIN(AVPROBE_SCORE_EXTENSION / 2 - 1, score_max); @@ -545,6 +563,7 @@ } } + s->build_index = 1; s->duration = s->start_time = AV_NOPTS_VALUE; av_strlcpy(s->filename, filename ? filename : "", sizeof(s->filename)); @@ -793,6 +812,9 @@ } st = s->streams[pkt->stream_index]; + + if (!st) + return -1; if (update_wrap_reference(s, st, pkt->stream_index, pkt) && st->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET) { // correct first time stamps to negative values @@ -883,6 +905,10 @@ *pnum = 0; *pden = 0; + + if (!st || !st->codec) + return; + switch (st->codec->codec_type) { case AVMEDIA_TYPE_VIDEO: if (st->r_frame_rate.num && !pc) { @@ -1013,6 +1039,9 @@ int64_t shift; int i, delay; + if (!st) + return; + if (st->first_dts != AV_NOPTS_VALUE || dts == AV_NOPTS_VALUE || st->cur_dts == AV_NOPTS_VALUE || @@ -1075,6 +1104,7 @@ cur_dts -= duration; } } +#if 0 // Very verbose: ffmpeg ticket 1344 if (pktl && pktl->pkt.dts != st->first_dts) { av_log(s, AV_LOG_DEBUG, "first_dts %s not matching first dts %s (pts %s, duration %d) in the queue\n", av_ts2str(st->first_dts), av_ts2str(pktl->pkt.dts), av_ts2str(pktl->pkt.pts), pktl->pkt.duration); @@ -1084,6 +1114,7 @@ av_log(s, AV_LOG_DEBUG, "first_dts %s but no packet with dts in the queue\n", av_ts2str(st->first_dts)); return; } +#endif pktl = s->packet_buffer ? s->packet_buffer : s->parse_queue; st->first_dts = cur_dts; } else if (st->cur_dts != RELATIVE_TS_BASE) @@ -1422,6 +1453,11 @@ return 0; } +/** + * Simply sets data pointer to null. + * + * This will leak memory if no one else frees the memory used by the packet. + */ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) { int ret = 0, i, got_packet = 0; @@ -1432,6 +1468,8 @@ AVStream *st; AVPacket cur_pkt; + av_init_packet(&cur_pkt); + /* read next packet */ ret = ff_read_packet(s, &cur_pkt); if (ret < 0) { @@ -1440,7 +1478,7 @@ /* flush the parsers */ for (i = 0; i < s->nb_streams; i++) { st = s->streams[i]; - if (st->parser && st->need_parsing) + if (st && st->codec && st->parser && st->need_parsing) parse_packet(s, NULL, st->index); } /* all remaining packets are now in parse_queue => @@ -1468,7 +1506,8 @@ av_ts2str(cur_pkt.dts), cur_pkt.size, cur_pkt.duration, cur_pkt.flags); - if (st->need_parsing && !st->parser && !(s->flags & AVFMT_FLAG_NOPARSE)) { + if (st && st->codec && st->need_parsing && !st->parser && + !(s->flags & AVFMT_FLAG_NOPARSE)) { st->parser = av_parser_init(st->codec->codec_id); if (!st->parser) { av_log(s, AV_LOG_VERBOSE, "parser not found for codec " @@ -1488,6 +1527,7 @@ /* no parsing needed: we just output the packet as is */ *pkt = cur_pkt; compute_pkt_fields(s, st, NULL, pkt); + st->got_frame = 0; if ((s->iformat->flags & AVFMT_GENERIC_INDEX) && (pkt->flags & AV_PKT_FLAG_KEY) && pkt->dts != AV_NOPTS_VALUE) { ff_reduce_index(s, st->index); @@ -1636,7 +1676,7 @@ } /* XXX: suppress the packet queue */ -static void flush_packet_queue(AVFormatContext *s) +void flush_packet_queue(AVFormatContext *s) { free_packet_buffer(&s->parse_queue, &s->parse_queue_end); free_packet_buffer(&s->packet_buffer, &s->packet_buffer_end); @@ -1648,6 +1688,9 @@ /*******************************************************/ /* seek support */ +/** + * @brief Finds the index of the first video stream within the AVFormatContext. + */ int av_find_default_stream_index(AVFormatContext *s) { int first_audio_index = -1; @@ -1685,6 +1728,7 @@ av_parser_close(st->parser); st->parser = NULL; } + st->got_frame = 0; st->last_IP_pts = AV_NOPTS_VALUE; st->last_dts_for_order_check = AV_NOPTS_VALUE; if (st->first_dts == AV_NOPTS_VALUE) @@ -2429,10 +2473,15 @@ "Estimating duration from bitrate, this may be inaccurate\n"); } +/** Maximum number of bytes we read to determine timing from PTS stream. */ #define DURATION_MAX_READ_SIZE 250000LL #define DURATION_MAX_RETRY 4 -/* only usable for MPEG-PS streams */ +/** + * @brief Estimates timings using PTS stream. + * + * only usable for MPEG-PS streams + */ static void estimate_timings_from_pts(AVFormatContext *ic, int64_t old_offset) { AVPacket pkt1, *pkt = &pkt1; @@ -2480,6 +2529,13 @@ if (ret != 0) break; read_size += pkt->size; + + if (pkt->stream_index >= ic->nb_streams) + { + av_free_packet(pkt); + continue; + } + st = ic->streams[pkt->stream_index]; if (pkt->pts != AV_NOPTS_VALUE && (st->start_time != AV_NOPTS_VALUE || @@ -2517,7 +2573,15 @@ } } -static void estimate_timings(AVFormatContext *ic, int64_t old_offset) +/** + * @brief Attempts to estimate timings using whatever means possible. + * + * First, if the stream is an MPEG-PS stream we try to find the PTS stream. + * Then we try to get the timings form one of the A/V streams. + * Finally, we just guess at the timings based on the estimated bit rate. + * + */ +void estimate_timings(AVFormatContext *ic, int64_t old_offset) { int64_t file_size; @@ -3003,6 +3067,12 @@ } } +/* absolute maximum size we read until we abort */ +#define MAX_READ_SIZE 5000000 + +/** Number of frames to read, max. */ +#define MAX_FRAMES 45 + int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) { int i, count, ret = 0, j; @@ -3013,6 +3083,9 @@ // new streams might appear, no options for those int orig_nb_streams = ic->nb_streams; int flush_codecs = ic->probesize > 0; + int hasaudio = 0; + int hasvideo = 0; + int read_packets = 0; if (ic->pb) av_log(ic, AV_LOG_DEBUG, "Before avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d\n", @@ -3071,11 +3144,14 @@ } for (i = 0; i < ic->nb_streams; i++) { + if (ic->streams[i]->info ) + { #if FF_API_R_FRAME_RATE ic->streams[i]->info->last_dts = AV_NOPTS_VALUE; #endif ic->streams[i]->info->fps_first_dts = AV_NOPTS_VALUE; ic->streams[i]->info->fps_last_dts = AV_NOPTS_VALUE; + } } count = 0; @@ -3112,21 +3188,32 @@ if (st->parser && st->parser->parser->split && !st->codec->extradata) break; - if (st->first_dts == AV_NOPTS_VALUE && - (st->codec->codec_type == AVMEDIA_TYPE_VIDEO || - st->codec->codec_type == AVMEDIA_TYPE_AUDIO)) + if (st->first_dts == AV_NOPTS_VALUE && st->codec->codec_id != AV_CODEC_ID_DSMCC_B) break; + if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) + hasvideo = 1; + else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) + hasaudio = 1; } if (i == ic->nb_streams) { /* NOTE: If the format has no header, then we need to read some * packets to get most of the streams, so we cannot stop here. */ - if (!(ic->ctx_flags & AVFMTCTX_NOHEADER)) { + if (!(ic->ctx_flags & AVFMTCTX_NOHEADER) || + (read_size >= MAX_READ_SIZE || read_packets >= MAX_FRAMES) || + (hasvideo && hasaudio)) { /* If we found the info for all the codecs, we can stop. */ ret = count; av_log(ic, AV_LOG_DEBUG, "All info found\n"); flush_codecs = 0; break; } + /* Is this is an MHEG only stream? Then we really can stop. */ + if (i == 1 && ic->streams[0]->codec->codec_id == AV_CODEC_ID_DSMCC_B) + { + ret = count; + av_log(ic, AV_LOG_DEBUG, "All DSM info found\n"); + break; + } } /* We did not get all the codec info, but we read too much data. */ if (read_size >= ic->probesize) { @@ -3147,6 +3234,9 @@ /* NOTE: A new stream can be added there if no header in file * (AVFMTCTX_NOHEADER). */ ret = read_frame_internal(ic, &pkt1); + + read_packets++; + if (ret == AVERROR(EAGAIN)) continue; diff -ruN ./libavformat/utils-mythtv.c /tmp/mythtv/mythtv/external/FFmpeg/libavformat/utils-mythtv.c --- ./libavformat/utils-mythtv.c 1970-01-01 01:00:00.000000000 +0100 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavformat/utils-mythtv.c 2014-08-09 07:18:01.794293197 +0200 @@ -0,0 +1,160 @@ +/* + * MPEG2 transport utilities + * Copyright (c) 2002-2012 The MythTV Team + * + * This file is part of MythTV. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "mpegts-mythtv.h" + +#ifdef _WIN32 +#define gmtime_r(X, Y) (memcpy(Y, gmtime(X), sizeof(struct tm)), Y) +#define localtime_r(X, Y) (memcpy(Y, localtime(X), sizeof(struct tm)), Y) +#endif + + +/** + * @brief Add a stream to an MPEG media stream. + * + * This is used by mpegts instead of av_new_stream, so we can + * track new streams as indicated by the PMT. + * + * @param s MPEG media stream handle + * @param st new media stream + * @param id file format dependent stream id + */ +AVStream *av_add_stream(AVFormatContext *s, AVStream *st, int id) +{ + int i; + + if (!st) + { + av_log(s, AV_LOG_ERROR, "av_add_stream: Error, AVStream is NULL"); + return NULL; + } + + av_remove_stream(s, id, 0); + + if (s->nb_streams >= MAX_STREAMS) + { + av_log(s, AV_LOG_ERROR, "av_add_stream: Error, (s->nb_streams >= MAX_STREAMS)"); + return NULL; + } + + if (s->iformat) { + /* no default bitrate if decoding */ + st->codec->bit_rate = 0; + } + st->index = s->nb_streams; + st->id = id; + st->start_time = AV_NOPTS_VALUE; + st->duration = AV_NOPTS_VALUE; + st->cur_dts = AV_NOPTS_VALUE; + st->first_dts = AV_NOPTS_VALUE; + + /* default pts settings is MPEG like */ + av_set_pts_info(st, 33, 1, 90000); + st->last_IP_pts = AV_NOPTS_VALUE; + for(i=0; ipts_buffer[i]= AV_NOPTS_VALUE; + + s->streams[s->nb_streams++] = st; + return st; +} + + +/** + * @brief Remove a stream from a media stream. + * + * This is used by mpegts, so we can track streams as indicated by the PMT. + * + * @param s MPEG media stream handle + * @param id stream id of stream to remove + * @param remove_ts if true, remove any matching MPEG-TS filter as well + */ +void av_remove_stream(AVFormatContext *s, int id, int remove_ts) { + int i; + int changes = 0; + + for (i=0; inb_streams; i++) { + if (s->streams[i]->id != id) + continue; + + av_log(NULL, AV_LOG_DEBUG, "av_remove_stream 0x%x\n", id); + + /* close codec context */ + AVCodecContext *codec_ctx = s->streams[i]->codec; + if (codec_ctx->codec) { + avcodec_close(codec_ctx); + av_free(codec_ctx); + } +#if 0 + /* make sure format context is not using the codec context */ + if (&s->streams[i] == s->cur_st) { + av_log(NULL, AV_LOG_DEBUG, "av_remove_stream cur_st = NULL\n"); + s->cur_st = NULL; + } +#endif + /* else if (s->cur_st > &s->streams[i]) { + av_log(NULL, AV_LOG_DEBUG, "av_remove_stream cur_st -= 1\n"); + s->cur_st -= sizeof(AVFormatContext *); + } */ + else { + av_log(NULL, AV_LOG_DEBUG, + "av_remove_stream: no change to cur_st\n"); + } + + av_log(NULL, AV_LOG_DEBUG, "av_remove_stream: removing... " + "s->nb_streams=%d i=%d\n", s->nb_streams, i); + /* actually remove av stream */ + s->nb_streams--; + if ((s->nb_streams - i) > 0) { + memmove(&s->streams[i], &s->streams[i+1], + (s->nb_streams-i)*sizeof(AVFormatContext *)); + } + else + s->streams[i] = NULL; + + /* remove ts filter if remove ts is true and + * the format decoder is the "mpegts" decoder + */ + if (remove_ts && s->iformat && s->priv_data && + (0 == strncmp(s->iformat->name, "mpegts", 6))) { + av_log(NULL, AV_LOG_DEBUG, + "av_remove_stream: mpegts_remove_stream\n"); + MpegTSContext *context = (MpegTSContext*) s->priv_data; + mpegts_remove_stream(context, id); + } + changes = 1; + } + if (changes) + { + // flush queued packets after a stream change (might need to make smarter) + flush_packet_queue(s); + + /* renumber the streams */ + av_log(NULL, AV_LOG_DEBUG, "av_remove_stream: renumbering streams\n"); + for (i=0; inb_streams; i++) + s->streams[i]->index=i; + } +} + +void av_estimate_timings(AVFormatContext *ic, int64_t old_offset) +{ + return estimate_timings(ic, old_offset); +} diff -ruN ./libavformat/wtvdec.c /tmp/mythtv/mythtv/external/FFmpeg/libavformat/wtvdec.c --- ./libavformat/wtvdec.c 2014-08-09 07:27:10.270281323 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavformat/wtvdec.c 2014-08-09 07:18:01.798293197 +0200 @@ -821,7 +821,7 @@ buf_size = FFMIN(len - consumed, sizeof(buf)); avio_read(pb, buf, buf_size); consumed += buf_size; - ff_parse_mpeg2_descriptor(s, st, 0, &pbuf, buf + buf_size, NULL, 0, 0, NULL); + ff_old_parse_mpeg2_descriptor(s, st, 0, &pbuf, buf + buf_size, NULL, 0, 0, NULL); } } else if (!ff_guidcmp(g, EVENTID_AudioTypeSpanningEvent)) { int stream_index = ff_find_stream_index(s, sid); diff -ruN ./libavutil/bswap.h /tmp/mythtv/mythtv/external/FFmpeg/libavutil/bswap.h --- ./libavutil/bswap.h 2014-08-09 07:27:10.278281322 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavutil/bswap.h 2014-08-09 07:18:01.806293197 +0200 @@ -78,6 +78,21 @@ } #endif +#ifndef av_bswap_dbl +static inline double bswap_dbl(double x) +{ + union { + uint32_t l[2]; + double d; + } w, r; + w.d = x; + r.l[0] = av_bswap32(w.l[1]); + r.l[1] = av_bswap32(w.l[0]); + return r.d; +} +#endif + + // be2ne ... big-endian to native-endian // le2ne ... little-endian to native-endian diff -ruN ./libavutil/frame.c /tmp/mythtv/mythtv/external/FFmpeg/libavutil/frame.c --- ./libavutil/frame.c 2014-08-09 07:27:10.282281322 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavutil/frame.c 2014-08-09 07:18:01.810293196 +0200 @@ -505,6 +505,13 @@ } } + /* MythTV ATSC Subtitle Support -- Begin */ + dst->atsc_cc_len = src->atsc_cc_len; + dst->scte_cc_len = src->scte_cc_len; + memcpy(dst->atsc_cc_buf, src->atsc_cc_buf, src->atsc_cc_len); + memcpy(dst->scte_cc_buf, src->scte_cc_buf, src->scte_cc_len); + /* MythTV ATSC Subtitle Support -- End */ + return 0; } diff -ruN ./libavutil/frame.h /tmp/mythtv/mythtv/external/FFmpeg/libavutil/frame.h --- ./libavutil/frame.h 2014-08-09 07:27:10.282281322 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libavutil/frame.h 2014-08-09 07:18:01.810293196 +0200 @@ -502,6 +502,20 @@ */ int channels; + /** ATSC CC data CEA-608/708 + * - encoding: unused + * - decoding: Set by libavcodec + */ + uint8_t atsc_cc_buf[1024];\ + int atsc_cc_len; + + /** SCTE CC data CEA-608 + * - encoding: unused + * - decoding: Set by libavcodec + */ + uint8_t scte_cc_buf[1024]; + int scte_cc_len; + /** * size of the corresponding packet containing the compressed * frame. It must be accessed using av_frame_get_pkt_size() and diff -ruN ./libpostproc/Makefile /tmp/mythtv/mythtv/external/FFmpeg/libpostproc/Makefile --- ./libpostproc/Makefile 2014-08-09 07:27:50.666280448 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libpostproc/Makefile 2014-08-09 07:18:01.822293196 +0200 @@ -3,6 +3,14 @@ NAME = postproc FFLIBS = avutil +# x86_32 needs -O1 -fomit-frame-pointer to compile inline asm +ifeq ($(ARCH_X86_32), yes) + CFLAGS += -fomit-frame-pointer + ifneq (, $(findstring debug, $(CCONFIG))) + CFLAGS += -O1 + endif +endif + HEADERS = postprocess.h \ version.h \ diff -ruN ./libpostproc/postprocess.c /tmp/mythtv/mythtv/external/FFmpeg/libpostproc/postprocess.c --- ./libpostproc/postprocess.c 2014-08-09 07:27:50.598280450 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/libpostproc/postprocess.c 2014-08-09 07:18:01.822293196 +0200 @@ -89,6 +89,8 @@ #include "postprocess_internal.h" #include "libavutil/avstring.h" +#include "libavutil/cpu.h" + unsigned postproc_version(void) { av_assert0(LIBPOSTPROC_VERSION_MICRO >= 100); @@ -900,6 +902,22 @@ PPContext *c= av_malloc(sizeof(PPContext)); int stride= FFALIGN(width, 16); //assumed / will realloc if needed int qpStride= (width+15)/16 + 2; //assumed / will realloc if needed + int cpuflags; + + if (CONFIG_RUNTIME_CPUDETECT && + !(cpuCaps & (PP_CPU_CAPS_MMX | PP_CPU_CAPS_MMX2 | + PP_CPU_CAPS_3DNOW | PP_CPU_CAPS_ALTIVEC ))) { + cpuflags = av_get_cpu_flags(); + + if (HAVE_MMX && cpuflags & AV_CPU_FLAG_MMX) + cpuCaps |= PP_CPU_CAPS_MMX; + if (HAVE_MMX2 && cpuflags & AV_CPU_FLAG_MMX2) + cpuCaps |= PP_CPU_CAPS_MMX2; + if (HAVE_AMD3DNOW && cpuflags & AV_CPU_FLAG_3DNOW) + cpuCaps |= PP_CPU_CAPS_3DNOW; + if (HAVE_ALTIVEC && cpuflags & AV_CPU_FLAG_ALTIVEC) + cpuCaps |= PP_CPU_CAPS_ALTIVEC; + } memset(c, 0, sizeof(PPContext)); c->av_class = &av_codec_context_class; diff -ruN ./library.mak /tmp/mythtv/mythtv/external/FFmpeg/library.mak --- ./library.mak 2014-08-09 07:27:10.214281324 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/library.mak 2014-08-09 07:18:01.822293196 +0200 @@ -1,4 +1,6 @@ -include $(SRC_PATH)/common.mak +SRC_DIR := $(SRC_PATH_BARE)/lib$(NAME) + +include $(SRC_PATH_BARE)/external/FFmpeg/common.mak LIBVERSION := $(lib$(NAME)_VERSION) LIBMAJOR := $(lib$(NAME)_VERSION_MAJOR) @@ -36,7 +38,8 @@ $(AR) $(ARFLAGS) $(AR_O) $^ $(RANLIB) $@ -install-headers: install-lib$(NAME)-headers install-lib$(NAME)-pkgconfig +#install-headers: install-lib$(NAME)-headers install-lib$(NAME)-pkgconfig +install-headers: install-lib$(NAME)-headers install-libs-$(CONFIG_STATIC): install-lib$(NAME)-static install-libs-$(CONFIG_SHARED): install-lib$(NAME)-shared diff -ruN ./Makefile /tmp/mythtv/mythtv/external/FFmpeg/Makefile --- ./Makefile 2014-08-09 07:27:13.918281244 +0200 +++ /tmp/mythtv/mythtv/external/FFmpeg/Makefile 2014-08-09 07:18:01.466293204 +0200 @@ -1,15 +1,17 @@ -MAIN_MAKEFILE=1 include config.mak -vpath %.c $(SRC_PATH) -vpath %.cpp $(SRC_PATH) -vpath %.h $(SRC_PATH) -vpath %.S $(SRC_PATH) -vpath %.asm $(SRC_PATH) -vpath %.rc $(SRC_PATH) -vpath %.v $(SRC_PATH) -vpath %.texi $(SRC_PATH) -vpath %/fate_config.sh.template $(SRC_PATH) +SRC_PATH = ${SRC_PATH_BARE}/external/FFmpeg +SRC_DIR = $(SRC_PATH) + +vpath %.c $(SRC_DIR) +vpath %.cpp $(SRC_DIR) +vpath %.h $(SRC_DIR) +vpath %.S $(SRC_DIR) +vpath %.asm $(SRC_DIR) +vpath %.rc $(SRC_DIR) +vpath %.v $(SRC_DIR) +vpath %.texi $(SRC_DIR) +vpath %/fate_config.sh.template $(SRC_DIR) AVPROGS-$(CONFIG_FFMPEG) += ffmpeg AVPROGS-$(CONFIG_FFPLAY) += ffplay @@ -33,6 +35,7 @@ HOSTPROGS := $(TESTTOOLS:%=tests/%) doc/print_options TOOLS = qt-faststart trasher uncoded_frame TOOLS-$(CONFIG_ZLIB) += cws2fws +MYTHPROGS = $(addprefix myth, ${PROGS}) FFLIBS-$(CONFIG_AVDEVICE) += avdevice FFLIBS-$(CONFIG_AVFILTER) += avfilter @@ -50,7 +53,7 @@ SKIPHEADERS = cmdutils_common_opts.h compat/w32pthreads.h -include $(SRC_PATH)/common.mak +include common.mak FF_EXTRALIBS := $(FFEXTRALIBS) FF_DEP_LIBS := $(DEP_LIBS) @@ -64,6 +67,9 @@ tools/uncoded_frame$(EXESUF): $(FF_DEP_LIBS) tools/uncoded_frame$(EXESUF): ELIBS = $(FF_EXTRALIBS) +${MYTHPROGS}: myth%: % + $(CP) $< $@ + config.h: .config .config: $(wildcard $(FFLIBS:%=$(SRC_PATH)/lib%/all*.c)) @-tput bold 2>/dev/null @@ -86,9 +92,9 @@ define DOSUBDIR $(foreach V,$(SUBDIR_VARS),$(eval $(call RESET,$(V)))) SUBDIR := $(1)/ -include $(SRC_PATH)/$(1)/Makefile --include $(SRC_PATH)/$(1)/$(ARCH)/Makefile -include $(SRC_PATH)/library.mak +include $(SRC_PATH_BARE)/external/FFmpeg/$(1)/Makefile +-include $(SRC_PATH_BARE)/external/FFmpeg/$(1)/$(ARCH)/Makefile +include $(SRC_PATH_BARE)/external/FFmpeg/library.mak endef $(foreach D,$(FFLIBS),$(eval $(call DOSUBDIR,lib$(D)))) @@ -119,8 +125,8 @@ -include $(wildcard tools/*.d) -VERSION_SH = $(SRC_PATH)/version.sh -GIT_LOG = $(SRC_PATH)/.git/logs/HEAD +VERSION_SH = $(SRC_PATH_BARE)/external/FFmpeg/version.sh +GIT_LOG = $(SRC_PATH_BARE)/.git/logs/HEAD .version: $(wildcard $(GIT_LOG)) $(VERSION_SH) config.mak .version: M=@ @@ -133,7 +139,7 @@ -include .version ifdef AVPROGS -install: install-progs install-data +install: install-mythprogs endif install: install-libs install-headers @@ -147,6 +153,10 @@ $(Q)mkdir -p "$(BINDIR)" $(INSTALL) -c -m 755 $(INSTPROGS) "$(BINDIR)" +install-mythprogs: install-progs-yes $(MYTHPROGS) + $(Q)mkdir -p "$(BINDIR)" + $(INSTALL) -c -m 755 $(MYTHPROGS) "$(BINDIR)" + install-data: $(DATA_FILES) $(EXAMPLES_FILES) $(Q)mkdir -p "$(DATADIR)/examples" $(INSTALL) -m 644 $(DATA_FILES) "$(DATADIR)" @@ -174,9 +184,9 @@ config: $(SRC_PATH)/configure $(value FFMPEG_CONFIGURATION) -check: all alltools examples testprogs fate +#check: all alltools examples testprogs fate -include $(SRC_PATH)/tests/Makefile +#include $(SRC_PATH)/tests/Makefile $(sort $(OBJDIRS)): $(Q)mkdir -p $@ diff -ruN ./README.sync /tmp/mythtv/mythtv/external/FFmpeg/README.sync --- ./README.sync 1970-01-01 01:00:00.000000000 +0100 +++ /tmp/mythtv/mythtv/external/FFmpeg/README.sync 2014-08-09 07:18:01.466293204 +0200 @@ -0,0 +1,69 @@ +This code was resynced upstream to: + +git://source.ffmpeg.org/ffmpeg.git at SHA1 d3db8988 on March 31, 2012 (Beirdo) +git://source.ffmpeg.org/ffmpeg.git at SHA1 ea5dab58 on May 23, 2012 (jya) +git://source.ffmpeg.org/ffmpeg.git at SHA1 cc4d80c on June 1st, 2012 (jya) +git://source.ffmpeg.org/ffmpeg.git at SHA1 f218121 on June 1st, 2012 (jya) +git://source.ffmpeg.org/ffmpeg.git at SHA1 59d765e3 on December 12th, 2012 (jya) (release/1.0 branch) +git://source.ffmpeg.org/ffmpeg.git at SHA1 553c9c77 on December 16th, 2012 (jya) (release/1.0 branch) +git://source.ffmpeg.org/ffmpeg.git at SHA1 057051b8 on February 17th, 2013 (jya) (release/1.1 branch) +git://source.ffmpeg.org/ffmpeg.git at SHA1 7c8beec4 on March 7th, 2013 (jya) (release/1.1 branch) +git://source.ffmpeg.org/ffmpeg.git at SHA1 fc7071cb on March 30th, 2013 (jya) (release/1.1 branch) +git://source.ffmpeg.org/ffmpeg.git at SHA1 33d699a4 on May 12th, 2013 (jya) (release/1.2 branch) +git://source.ffmpeg.org/ffmpeg.git at SHA1 59549b5a on June 20th, 2013 (jya) (release/1.2 branch) +git://source.ffmpeg.org/ffmpeg.git at SHA1 c9ea1f7f on July 16th, 2013 (jya) (release/1.2 branch) +git://source.ffmpeg.org/ffmpeg.git at SHA1 875649bf on July 27th, 2013 (jya) (release/1.2 branch) +git://source.ffmpeg.org/ffmpeg.git at SHA1 f9c87262 on August 12th, 2013 (jya) (release/1.2 branch) +git://source.ffmpeg.org/ffmpeg.git at SHA1 3d79041f on May 4th, 2014 (jya) (release/1.2 branch, release 1.2.6)) +git://source.ffmpeg.org/ffmpeg.git at n2.2.2-30-gacafd18 on May 30th, 2014 (jya) (release/2.2 branch, release 2.2.2)) +git://source.ffmpeg.org/ffmpeg.git at n2.2.3 on May 6th, 2014 (jya) (release/2.2 branch, release 2.2.3)) +git://source.ffmpeg.org/ffmpeg.git at n2.2.3 on July 14th, 2014 (jya) (release/2.2 branch, release 2.2.4)) + +List of files modified from original FFmpeg: +Makefile +README.sync +common.mak +configure +libavcodec/Makefile +libavcodec/avcodec.h // add subtitle support +libavcodec/codec_desc.c // add subtitle codec description +libavcodec/dvbsubdec.c // DVB subtitles fixes +libavcodec/dvbsub_parser.c // Fix for #5978 +libavcodec/dvdsubdec.c +libavcodec/get_bits.h +libavcodec/golomb.h +libavcodec/h264_refs.c +libavcodec/libavcodec.v +libavcodec/mpeg12dec.c // CEA-708/608 support +libavcodec/mpeg4videodec.c // fixes for #3001 + fix for h263 frame rate +libavcodec/mpeg4videoenc.c +libavcodec/mpegaudio_parser.c // add support for dual language +libavcodec/mpegaudio_template.c // small change to reduce verbosity +libavcodec/mpegaudiodec.c +libavcodec/mpegaudiodecheader.c // Add support for dual language detection +libavcodec/mpegaudiodecheader.h +libavcodec/mpegvideo.c // CEA-708/608 support +libavcodec/mpegvideo.h // rename member as it cause compilation with C++, CEA-708/608 support +libavcodec/utils-mythtv.c <- new file +libavcodec/pcm.c // Fix to allow playing stream marked with 0 channels +libavcodec/pgssubdec.c // support for forced display of PGS subtitle +libavformat/Makefile +libavformat/allformats.c // add MythTV mpegts demuxer +libavformat/avformat.h // extra field to all callbacks for stream change +libavformat/mov.c // Fix http streaming of iPhone videos (Fix from xbmc) +libavformat/mpeg.c // support for AAC/LATM, mpeg2vbi stream and stream change callback +libavformat/mpeg.h // AAC/LATM stream type definition +libavformat/mpegts-mythtv.c <- new file +libavformat/mpegts-mythtv.h <- new file +libavformat/mpegts.c // rename some functions so they do not conflict with mythtv mpegts demuxer +libavformat/mpegts.h +libavformat/utils-mythtv.c <- new file +libavformat/utils.c // fix to make Myth mpegts demux higher priority than FFmpeg's own, some minor diff to prevent corner cases crash +libavformat/rtpdec_mpegts.c <- header change +libavformat/rtsp.c <- header change +libavformat/wtvdec.c <- header change +libavutil/bswap.h +libavutil/frame.c // CEA-708/608 AVFrame members addition +libpostproc/Makefile +libpostproc/postprocess.c +library.mak // change so it doesn't install pkgconfig info