summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/combined/ffmpeg/ff_video_decoder.c89
1 files changed, 82 insertions, 7 deletions
diff --git a/src/combined/ffmpeg/ff_video_decoder.c b/src/combined/ffmpeg/ff_video_decoder.c
index 75fd862c4..f4f7175fa 100644
--- a/src/combined/ffmpeg/ff_video_decoder.c
+++ b/src/combined/ffmpeg/ff_video_decoder.c
@@ -78,6 +78,9 @@ struct ff_video_decoder_s {
xine_stream_t *stream;
int64_t pts;
+ uint64_t pts_tag_mask;
+ uint64_t pts_tag;
+ int pts_tag_counter;
int video_step;
uint8_t decoder_ok:1;
@@ -1174,6 +1177,42 @@ static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *bu
}
}
+static uint64_t ff_tag_pts(ff_video_decoder_t *this, uint64_t pts)
+{
+ return pts | this->pts_tag;
+}
+
+static uint64_t ff_untag_pts(ff_video_decoder_t *this, uint64_t pts)
+{
+ if (this->pts_tag_mask == 0)
+ return pts; /* pts tagging inactive */
+
+ if (this->pts_tag != 0 && (pts & this->pts_tag_mask) != this->pts_tag)
+ return 0; /* reset pts if outdated while waiting for first pass (see below) */
+
+ return pts & ~this->pts_tag_mask;
+}
+
+static void ff_check_pts_tagging(ff_video_decoder_t *this, uint64_t pts)
+{
+ if (this->pts_tag_mask == 0)
+ return; /* pts tagging inactive */
+
+ if ((pts & this->pts_tag_mask) != this->pts_tag)
+ return; /* pts still outdated */
+
+ if (this->pts_tag != 0) {
+ /* first pass: reset pts_tag */
+ this->pts_tag = 0;
+ } else if (pts == 0)
+ return; /* cannot detect second pass */
+ else {
+ /* second pass: reset pts_tag_mask and pts_tag_counter */
+ this->pts_tag_mask = 0;
+ this->pts_tag_counter = 0;
+ }
+}
+
static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) {
uint8_t *chunk_buf = this->buf;
AVRational avr00 = {0, 1};
@@ -1200,8 +1239,8 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) {
if (this->size == 0) {
/* take over pts when we are about to buffer a frame */
- this->av_frame->reordered_opaque = this->pts;
- this->context->reordered_opaque = this->pts;
+ this->av_frame->reordered_opaque = ff_tag_pts(this, this->pts);
+ this->context->reordered_opaque = ff_tag_pts(this, this->pts);
this->pts = 0;
}
@@ -1259,7 +1298,7 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) {
this->size);
/* reset consumed pts value */
- this->context->reordered_opaque = 0;
+ this->context->reordered_opaque = ff_tag_pts(this, 0);
lprintf("consumed size: %d, got_picture: %d\n", len, got_picture);
if ((len <= 0) || (len > this->size)) {
@@ -1278,8 +1317,8 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) {
chunk_buf = this->buf;
/* take over pts for next access unit */
- this->av_frame->reordered_opaque = this->pts;
- this->context->reordered_opaque = this->pts;
+ this->av_frame->reordered_opaque = ff_tag_pts(this, this->pts);
+ this->context->reordered_opaque = ff_tag_pts(this, this->pts);
this->pts = 0;
}
}
@@ -1375,7 +1414,8 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) {
ff_convert_frame(this, img);
}
- img->pts = this->av_frame->reordered_opaque;
+ img->pts = ff_untag_pts(this, this->av_frame->reordered_opaque);
+ ff_check_pts_tagging(this, this->av_frame->reordered_opaque); /* only check for valid frames */
this->av_frame->reordered_opaque = 0;
/* workaround for weird 120fps streams */
@@ -1416,7 +1456,7 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) {
this->output_format,
VO_BOTH_FIELDS|this->frame_flags);
/* set PTS to allow early syncing */
- img->pts = this->av_frame->reordered_opaque;
+ img->pts = ff_untag_pts(this, this->av_frame->reordered_opaque);
this->av_frame->reordered_opaque = 0;
img->duration = video_step_to_use;
@@ -1502,6 +1542,10 @@ static void ff_reset (video_decoder_t *this_gen) {
if (this->is_mpeg12)
mpeg_parser_reset(this->mpeg_parser);
+
+ this->pts_tag_mask = 0;
+ this->pts_tag = 0;
+ this->pts_tag_counter = 0;
}
static void ff_discontinuity (video_decoder_t *this_gen) {
@@ -1509,6 +1553,37 @@ static void ff_discontinuity (video_decoder_t *this_gen) {
lprintf ("ff_discontinuity\n");
this->pts = 0;
+
+ /*
+ * there is currently no way to reset all the pts which are stored in the decoder.
+ * therefore, we add a unique tag (generated from pts_tag_counter) to pts (see
+ * ff_tag_pts()) and wait for it to appear on returned frames.
+ * until then, any retrieved pts value will be reset to 0 (see ff_untag_pts()).
+ * when we see the tag returned, pts_tag will be reset to 0. from now on, any
+ * untagged pts value is valid already.
+ * when tag 0 appears too, there are no tags left in the decoder so pts_tag_mask
+ * and pts_tag_counter will be reset to 0 too (see ff_check_pts_tagging()).
+ */
+ this->pts_tag_counter++;
+ this->pts_tag_mask = 0;
+ this->pts_tag = 0;
+ {
+ /* pts values typically don't use the uppermost bits. therefore we put the tag there */
+ int counter_mask = 1;
+ uint64_t tag_mask = 0x8000000000000000ull;
+ while (this->pts_tag_counter >= counter_mask)
+ {
+ /*
+ * mirror the counter into the uppermost bits. this allows us to enlarge mask as
+ * necessary and while previous taggings can still be detected to be outdated.
+ */
+ if (this->pts_tag_counter & counter_mask)
+ this->pts_tag |= tag_mask;
+ this->pts_tag_mask |= tag_mask;
+ tag_mask >>= 1;
+ counter_mask <<= 1;
+ }
+ }
}
static void ff_dispose (video_decoder_t *this_gen) {