diff --git a/packages/multimedia/ffmpeg/patches/ffmpeg-0.10.6-0035-Provide-a-monotonic-timestamp-to-the-outside-world.patch b/packages/multimedia/ffmpeg/patches/ffmpeg-0.10.6-0035-Provide-a-monotonic-timestamp-to-the-outside-world.patch new file mode 100644 index 0000000000..1db0d7efd7 --- /dev/null +++ b/packages/multimedia/ffmpeg/patches/ffmpeg-0.10.6-0035-Provide-a-monotonic-timestamp-to-the-outside-world.patch @@ -0,0 +1,337 @@ +From b2d3b83a73d71833f84485adb8a6f4c0959b4c4e Mon Sep 17 00:00:00 2001 +From: Harald Axmann +Date: Sun, 16 Dec 2012 16:07:36 +0100 +Subject: [PATCH] lavf: Provide a monotonic timestamp to the outside world + +This detects and removes single timestamp wraps, which fixes seeking for affected video files. +--- + libavformat/avformat.h | 37 +++++++++++ + libavformat/options.c | 1 + + libavformat/utils.c | 123 ++++++++++++++++++++++++++++++++++--- + 3 files changed, 151 insertions(+), 10 deletions(-) + +diff --git a/libavformat/avformat.h b/libavformat/avformat.h +index c76ac71..40f71cb 100644 +--- a/libavformat/avformat.h ++++ b/libavformat/avformat.h +@@ -627,6 +627,13 @@ enum AVStreamParseType { + #define AV_DISPOSITION_VISUAL_IMPAIRED 0x0100 /**< stream for visual impaired audiences */ + #define AV_DISPOSITION_CLEAN_EFFECTS 0x0200 /**< stream without voice */ + ++/* ++ * Options for behavior on timestamp wrap detection. ++ */ ++#define AV_PTS_WRAP_IGNORE 0 ///< ignore the wrap ++#define AV_PTS_WRAP_ADD_OFFSET 1 ///< add the format specific offset on wrap detection ++#define AV_PTS_WRAP_SUB_OFFSET -1 ///< subtract the format specific offset on wrap detection ++ + /** + * Stream structure. + * New fields can be added to the end with minor version bumps. +@@ -857,6 +864,24 @@ enum AVStreamParseType { + + int pts_wrap_bits; /**< number of bits in pts (used for wrapping control) */ + #endif ++ ++ /** ++ * Internal data to check for wrapping of the time stamp ++ */ ++ int64_t pts_wrap_reference; ++ ++ /** ++ * Options for behavior, when a wrap is detected. ++ * ++ * Defined by AV_PTS_WRAP_ values. ++ * ++ * If correction is enabled, there are two possibilities: ++ * If the first time stamp is near the wrap point, the wrap offset ++ * will be subtracted, which will create negative time stamps. ++ * Otherwise the offset will be added. ++ */ ++ int pts_wrap_behavior; ++ + } AVStream; + + #define AV_PROGRAM_RUNNING 1 +@@ -878,6 +903,10 @@ enum AVStreamParseType { + int program_num; + int pmt_pid; + int pcr_pid; ++ ++ int64_t pts_wrap_reference; ///< reference dts for wrap detection ++ int pts_wrap_behavior; ///< behavior on wrap detection ++ + } AVProgram; + + #define AVFMTCTX_NOHEADER 0x0001 /**< signal that no header is present +@@ -1234,6 +1263,14 @@ enum AVStreamParseType { + /* av_seek_frame() support */ + int64_t data_offset; /**< offset of the first packet */ + #endif ++ ++ /** ++ * Correct single timestamp overflows ++ * - encoding: unused ++ * - decoding: Set by user via AVOPtions (NO direct access) ++ */ ++ unsigned int correct_ts_overflow; ++ + } AVFormatContext; + + typedef struct AVPacketList { +diff --git a/libavformat/options.c b/libavformat/options.c +index 31acbd3..666d05e 100644 +--- a/libavformat/options.c ++++ b/libavformat/options.c +@@ -127,6 +127,7 @@ static const AVClass *format_child_class_next(const AVClass *prev) + {"careful", "consider things that violate the spec and have not been seen in the wild as errors", 0, AV_OPT_TYPE_CONST, {.dbl = AV_EF_CAREFUL }, INT_MIN, INT_MAX, D, "err_detect"}, + {"compliant", "consider all spec non compliancies as errors", 0, AV_OPT_TYPE_CONST, {.dbl = AV_EF_COMPLIANT }, INT_MIN, INT_MAX, D, "err_detect"}, + {"aggressive", "consider things that a sane encoder shouldnt do as an error", 0, AV_OPT_TYPE_CONST, {.dbl = AV_EF_AGGRESSIVE }, INT_MIN, INT_MAX, D, "err_detect"}, ++{"correct_ts_overflow", "correct single timestamp overflows", OFFSET(correct_ts_overflow), AV_OPT_TYPE_INT, {.dbl = 1}, 0, 1, D}, + {NULL}, + }; + +diff --git a/libavformat/utils.c b/libavformat/utils.c +index 63f89dd..89e5ed4 100644 +--- a/libavformat/utils.c ++++ b/libavformat/utils.c +@@ -122,6 +122,27 @@ static void frac_add(AVFrac *f, int64_t incr) + f->num = num; + } + ++/** ++ * Wrap a given time stamp, if there is an indication for an overflow ++ * ++ * @param st stream ++ * @param timestamp the time stamp to wrap ++ * @return resulting time stamp ++ */ ++static int64_t wrap_timestamp(AVStream *st, int64_t timestamp) ++{ ++ if (st->pts_wrap_behavior != AV_PTS_WRAP_IGNORE && ++ st->pts_wrap_reference != AV_NOPTS_VALUE && timestamp != AV_NOPTS_VALUE) { ++ if (st->pts_wrap_behavior == AV_PTS_WRAP_ADD_OFFSET && ++ timestamp < st->pts_wrap_reference) ++ return timestamp + (1LL<pts_wrap_bits); ++ else if (st->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET && ++ timestamp >= st->pts_wrap_reference) ++ return timestamp - (1LL<pts_wrap_bits); ++ } ++ return timestamp; ++} ++ + /** head of registered input format linked list */ + static AVInputFormat *first_iformat = NULL; + /** head of registered output format linked list */ +@@ -798,6 +819,8 @@ int av_read_packet(AVFormatContext *s, AVPacket *pkt) + } + + st= s->streams[pkt->stream_index]; ++ pkt->dts = wrap_timestamp(st, pkt->dts); ++ pkt->pts = wrap_timestamp(st, pkt->pts); + + switch(st->codec->codec_type){ + case AVMEDIA_TYPE_VIDEO: +@@ -948,8 +971,67 @@ static int is_intra_only(AVCodecContext *enc){ + return 0; + } + ++static int update_wrap_reference(AVFormatContext *s, AVStream *st, int stream_index) ++{ ++ if (s->correct_ts_overflow && st->pts_wrap_bits != 64 && ++ st->pts_wrap_reference == AV_NOPTS_VALUE && st->first_dts != AV_NOPTS_VALUE) { ++ int i; ++ ++ // reference time stamp should be 60 s before first time stamp ++ int64_t pts_wrap_reference = st->first_dts - av_rescale(60, st->time_base.den, st->time_base.num); ++ // if first time stamp is not more than 1/8 and 60s before the wrap point, subtract rather than add wrap offset ++ int pts_wrap_behavior = (st->first_dts < (1LL<pts_wrap_bits) - (1LL<pts_wrap_bits-3)) || ++ (st->first_dts < (1LL<pts_wrap_bits) - av_rescale(60, st->time_base.den, st->time_base.num)) ? ++ AV_PTS_WRAP_ADD_OFFSET : AV_PTS_WRAP_SUB_OFFSET; ++ ++ AVProgram *first_program = av_find_program_from_stream(s, NULL, stream_index); ++ ++ if (!first_program) { ++ int default_stream_index = av_find_default_stream_index(s); ++ if (s->streams[default_stream_index]->pts_wrap_reference == AV_NOPTS_VALUE) { ++ for (i=0; inb_streams; i++) { ++ s->streams[i]->pts_wrap_reference = pts_wrap_reference; ++ s->streams[i]->pts_wrap_behavior = pts_wrap_behavior; ++ } ++ } ++ else { ++ st->pts_wrap_reference = s->streams[default_stream_index]->pts_wrap_reference; ++ st->pts_wrap_behavior = s->streams[default_stream_index]->pts_wrap_behavior; ++ } ++ } ++ else { ++ AVProgram *program = first_program; ++ while (program) { ++ if (program->pts_wrap_reference != AV_NOPTS_VALUE) { ++ pts_wrap_reference = program->pts_wrap_reference; ++ pts_wrap_behavior = program->pts_wrap_behavior; ++ break; ++ } ++ program = av_find_program_from_stream(s, program, stream_index); ++ } ++ ++ // update every program with differing pts_wrap_reference ++ program = first_program; ++ while(program) { ++ if (program->pts_wrap_reference != pts_wrap_reference) { ++ for (i=0; inb_stream_indexes; i++) { ++ s->streams[program->stream_index[i]]->pts_wrap_reference = pts_wrap_reference; ++ s->streams[program->stream_index[i]]->pts_wrap_behavior = pts_wrap_behavior; ++ } ++ ++ program->pts_wrap_reference = pts_wrap_reference; ++ program->pts_wrap_behavior = pts_wrap_behavior; ++ } ++ program = av_find_program_from_stream(s, program, stream_index); ++ } ++ } ++ return 1; ++ } ++ return 0; ++} ++ + static void update_initial_timestamps(AVFormatContext *s, int stream_index, +- int64_t dts, int64_t pts) ++ int64_t dts, int64_t pts, AVPacket *pkt) + { + AVStream *st= s->streams[stream_index]; + AVPacketList *pktl= s->packet_buffer; +@@ -973,6 +1055,16 @@ static void update_initial_timestamps(AVFormatContext *s, int stream_index, + if(st->start_time == AV_NOPTS_VALUE && pktl->pkt.pts != AV_NOPTS_VALUE) + st->start_time= pktl->pkt.pts; + } ++ ++ if (update_wrap_reference(s, st, stream_index) && st->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET) { ++ // correct first time stamps to negative values ++ st->first_dts = wrap_timestamp(st, st->first_dts); ++ st->cur_dts = wrap_timestamp(st, st->cur_dts); ++ pkt->dts = wrap_timestamp(st, pkt->dts); ++ pkt->pts = wrap_timestamp(st, pkt->pts); ++ pts = wrap_timestamp(st, pts); ++ } ++ + if (st->start_time == AV_NOPTS_VALUE) + st->start_time = pts; + } +@@ -1104,7 +1196,7 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, + /* PTS = presentation timestamp */ + if (pkt->dts == AV_NOPTS_VALUE) + pkt->dts = st->last_IP_pts; +- update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts); ++ update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts, pkt); + if (pkt->dts == AV_NOPTS_VALUE) + pkt->dts = st->cur_dts; + +@@ -1131,7 +1223,7 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, + /* presentation is not delayed : PTS and DTS are the same */ + if(pkt->pts == AV_NOPTS_VALUE) + pkt->pts = pkt->dts; +- update_initial_timestamps(s, pkt->stream_index, pkt->pts, pkt->pts); ++ update_initial_timestamps(s, pkt->stream_index, pkt->pts, pkt->pts, pkt); + if(pkt->pts == AV_NOPTS_VALUE) + pkt->pts = st->cur_dts; + pkt->dts = pkt->pts; +@@ -1147,7 +1239,7 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, + if(pkt->dts == AV_NOPTS_VALUE) + pkt->dts= st->pts_buffer[0]; + if(st->codec->codec_id == CODEC_ID_H264){ // we skipped it above so we try here +- update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts); // this should happen on the first packet ++ update_initial_timestamps(s, pkt->stream_index, pkt->dts, pkt->pts, pkt); // this should happen on the first packet + } + if(pkt->dts > st->cur_dts) + st->cur_dts = pkt->dts; +@@ -1550,6 +1642,7 @@ int ff_add_index_entry(AVIndexEntry **index_entries, + int av_add_index_entry(AVStream *st, + int64_t pos, int64_t timestamp, int size, int distance, int flags) + { ++ timestamp = wrap_timestamp(st, timestamp); + return ff_add_index_entry(&st->index_entries, &st->nb_index_entries, + &st->index_entries_allocated_size, pos, + timestamp, size, distance, flags); +@@ -1602,6 +1695,12 @@ int av_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts + } + #endif + ++static int64_t ff_read_timestamp(AVFormatContext *s, int stream_index, int64_t *ppos, int64_t pos_limit, ++ int64_t (*read_timestamp)(struct AVFormatContext *, int , int64_t *, int64_t )) ++{ ++ return wrap_timestamp(s->streams[stream_index], read_timestamp(s, stream_index, ppos, pos_limit)); ++} ++ + int ff_seek_frame_binary(AVFormatContext *s, int stream_index, int64_t target_ts, int flags) + { + AVInputFormat *avif= s->iformat; +@@ -1689,7 +1788,7 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, + + if(ts_min == AV_NOPTS_VALUE){ + pos_min = s->data_offset; +- ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX); ++ ts_min = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp); + if (ts_min == AV_NOPTS_VALUE) + return -1; + } +@@ -1705,7 +1804,7 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, + pos_max = filesize - 1; + do{ + pos_max -= step; +- ts_max = read_timestamp(s, stream_index, &pos_max, pos_max + step); ++ ts_max = ff_read_timestamp(s, stream_index, &pos_max, pos_max + step, read_timestamp); + step += step; + }while(ts_max == AV_NOPTS_VALUE && pos_max >= step); + if (ts_max == AV_NOPTS_VALUE) +@@ -1713,7 +1812,7 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, + + for(;;){ + int64_t tmp_pos= pos_max + 1; +- int64_t tmp_ts= read_timestamp(s, stream_index, &tmp_pos, INT64_MAX); ++ int64_t tmp_ts= ff_read_timestamp(s, stream_index, &tmp_pos, INT64_MAX, read_timestamp); + if(tmp_ts == AV_NOPTS_VALUE) + break; + ts_max= tmp_ts; +@@ -1760,7 +1859,7 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, + pos= pos_limit; + start_pos= pos; + +- ts = read_timestamp(s, stream_index, &pos, INT64_MAX); //may pass pos_limit instead of -1 ++ ts = ff_read_timestamp(s, stream_index, &pos, INT64_MAX, read_timestamp); //may pass pos_limit instead of -1 + if(pos == pos_max) + no_change++; + else +@@ -1788,9 +1887,9 @@ int64_t ff_gen_search(AVFormatContext *s, int stream_index, int64_t target_ts, + ts = (flags & AVSEEK_FLAG_BACKWARD) ? ts_min : ts_max; + #if 0 + pos_min = pos; +- ts_min = read_timestamp(s, stream_index, &pos_min, INT64_MAX); ++ ts_min = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp); + pos_min++; +- ts_max = read_timestamp(s, stream_index, &pos_min, INT64_MAX); ++ ts_max = ff_read_timestamp(s, stream_index, &pos_min, INT64_MAX, read_timestamp); + av_dlog(s, "pos=0x%"PRIx64" %"PRId64"<=%"PRId64"<=%"PRId64"\n", + pos, ts_min, target_ts, ts_max); + #endif +@@ -2992,6 +3091,8 @@ AVStream *avformat_new_stream(AVFormatContext *s, AVCodec *c) + st->cur_dts = 0; + st->first_dts = AV_NOPTS_VALUE; + st->probe_packets = MAX_PROBE_PACKETS; ++ st->pts_wrap_reference = AV_NOPTS_VALUE; ++ st->pts_wrap_behavior = AV_PTS_WRAP_IGNORE; + + /* default pts setting is MPEG-like */ + avpriv_set_pts_info(st, 33, 1, 90000); +@@ -3025,6 +3126,8 @@ AVProgram *av_new_program(AVFormatContext *ac, int id) + program->discard = AVDISCARD_NONE; + } + program->id = id; ++ program->pts_wrap_reference = AV_NOPTS_VALUE; ++ program->pts_wrap_behavior = AV_PTS_WRAP_IGNORE; + + return program; + } +-- +1.7.10 +