summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarren Salt <linux@youmustbejoking.demon.co.uk>2007-10-31 23:53:37 +0000
committerDarren Salt <linux@youmustbejoking.demon.co.uk>2007-10-31 23:53:37 +0000
commit753da2c501c6840ab50deaae1dd169f3feaf8f4d (patch)
tree6ad36a6015b45370480d19e005e1aa4485f708f9
parentbe3bdd90aff7622e81c51f81f7f21ea0685d73e2 (diff)
parente2aafb0f72934a33a9318faf411c79b4cf170428 (diff)
downloadxine-lib-753da2c501c6840ab50deaae1dd169f3feaf8f4d.tar.gz
xine-lib-753da2c501c6840ab50deaae1dd169f3feaf8f4d.tar.bz2
Merge from 1.1.
-rw-r--r--src/demuxers/demux_mpeg_pes.c112
-rw-r--r--src/libffmpeg/ff_video_decoder.c45
2 files changed, 103 insertions, 54 deletions
diff --git a/src/demuxers/demux_mpeg_pes.c b/src/demuxers/demux_mpeg_pes.c
index 9ef38f97d..705756dd6 100644
--- a/src/demuxers/demux_mpeg_pes.c
+++ b/src/demuxers/demux_mpeg_pes.c
@@ -136,13 +136,79 @@ static int32_t parse_IEC14496_FlexMux_stream(demux_mpeg_pes_t *this, uint8_t *p,
static int32_t parse_program_stream_directory(demux_mpeg_pes_t *this, uint8_t *p, buf_element_t *buf);
static int32_t parse_program_stream_pack_header(demux_mpeg_pes_t *this, uint8_t *p, buf_element_t *buf);
-static void check_newpts( demux_mpeg_pes_t *this, int64_t pts, int video )
+static int detect_pts_discontinuity( demux_mpeg_pes_t *this, int64_t pts, int video )
{
int64_t diff;
-
+
+ /* discontinuity detection is difficult to implement in the demuxer as it gets
+ * for example video packets in decoding order and there can be multiple audio
+ * and video tracks. So for simplicity, let's just deal with a single audio and
+ * a single video track.
+ *
+ * To start with, let's have a look at the audio and video track independently.
+ * Whenever pts differs from last_pts[video] by at least WRAP_THRESHOLD, a jump
+ * in pts is detected. Such a jump can happen for example when the pts counter
+ * overflows, as shown below (video decoding order ignored for simplicity; the
+ * variable values are shown after returning from the below function check_newpts;
+ * an asterisk means that this value has been cleared (see check_newpts)):
+ *
+ * pts: 7v 7a 8v 9v 9a : 0v 1v 1a 2v 3v 3a 4v
+ * last_pts[0]: 6 7 7 7 9 : * * 1 1 1 3 3
+ * last_pts[1]: 7 7 8 9 9 : 0 1 1 2 3 3 4
+ * | | |
+ * | | +--- audio pts wrap ignored
+ * | +--------- video pts wrap detected
+ * +----------- pts wrap boundary
+ */
diff = pts - this->last_pts[video];
-
- if( pts && (this->send_newpts || (this->last_pts[video] && abs(diff)>WRAP_THRESHOLD) ) ) {
+
+ if (this->last_pts[video] && abs(diff)>WRAP_THRESHOLD)
+ return 1;
+
+ /* but the above code can cause a huge delay while replaying when audio and video
+ * track are not aligned on a common pts wrap boundery, as shown below:
+ *
+ * pts: 7v 8v 7a 9v : 0v 9a 1v 2v : 1a 3v 4v 3a
+ * last_pts[0]: 6 6 7 7 : * 9 9 9 : 1 1 1 3
+ * last_pts[1]: 7 8 8 9 : 0 0 1 2 : * 3 4 4
+ * | | | | |
+ * | | | | +--- audio pts wrap detected
+ * | | | +----- audio pts wrap boundary
+ * | | +-------------- audio packet causes a huge delay
+ * | +----------------- video pts wrap detected
+ * +------------------- video pts wrap boundery
+ *
+ * So there is the need to compare audio track pts against video track pts
+ * to detect when pts values are in between pts wrap bounderies, where a
+ * jump needs to be detected too, as shown below:
+ *
+ * pts: 7v 8v 7a 9v : 0v 9a 1v 2v : 1a 3v 4v 3a
+ * last_pts[0]: 6 6 7 7 : * 9 * * : 1 1 1 3
+ * last_pts[1]: 7 8 8 9 : 0 * 1 2 : 2 3 4 4
+ * | | | | | |
+ * | | | | | +--- (audio pts wrap ignored)
+ * | | | | +----- audio pts wrap boundary
+ * | | | +----------- video pts wrap detected
+ * | | +-------------- audio pts wrap detected
+ * | +----------------- video pts wrap detected
+ * +------------------- (video pts wrap boundery)
+ *
+ * Basically, it's almost the same test like above, but against the other track's
+ * pts value and with a different limit. As the pts counter is a 33 bit unsigned
+ * integer, we choose 2^31 as limit (2^32 would require the tracks to be aligned).
+ */
+ diff = pts - this->last_pts[1-video];
+
+ if (this->last_pts[1-video] && abs(diff)>(1u<<31))
+ return 1;
+
+ /* no discontinuity detected */
+ return 0;
+}
+
+static void check_newpts( demux_mpeg_pes_t *this, int64_t pts, int video )
+{
+ if( pts && (this->send_newpts || detect_pts_discontinuity(this, pts, video) ) ) {
/* check if pts is outside nav pts range. any stream without nav must enter here. */
if( pts > this->nav_last_end_pts || pts < this->nav_last_start_pts )
@@ -159,47 +225,13 @@ static void check_newpts( demux_mpeg_pes_t *this, int64_t pts, int video )
} else {
lprintf("no wrap detected\n" );
}
-
+
+ /* clear pts on the other track to avoid detecting the same discontinuity again */
this->last_pts[1-video] = 0;
}
if( pts )
- {
- /* don't detect a discontinuity only for video respectively audio. It's also a discontinuity
- indication when audio and video pts differ to much e. g. when a pts wrap happens.
- The original code worked well when the wrap happend like this:
-
- V7 A7 V8 V9 A9 Dv V0 V1 da A1 V2 V3 A3 V4
-
- Legend:
- Vn = video packet with timestamp n
- An = audio packet with timestamp n
- Dv = discontinuity detected on following video packet
- Da = discontinuity detected on following audio packet
- dv = discontinuity detected on following video packet but ignored
- da = discontinuity detected on following audio packet but ignored
-
- But with a certain delay between audio and video packets (e. g. the way DVB-S broadcasts
- the packets) the code didn't work:
-
- V7 V8 A7 V9 Dv V0 _A9_ V1 V2 Da _A1_ V3 V4 A3
-
- Packet A9 caused audio to jump forward and A1 caused it to jump backward with inserting
- a delay of almoust 26.5 hours!
-
- The new code gives the following sequences for the above examples:
-
- V7 A7 V8 V9 A9 Dv V0 V1 A1 V2 V3 A3 V4
-
- V7 V8 A7 V9 Dv V0 Da A9 Dv V1 V2 A1 V3 V4 A3
-
- After proving this code it should be cleaned up to use just a single variable "last_pts". */
-
-/*
this->last_pts[video] = pts;
-*/
- this->last_pts[video] = this->last_pts[1-video] = pts;
- }
}
static off_t read_data(demux_mpeg_pes_t *this, uint8_t *buf, off_t nlen)
diff --git a/src/libffmpeg/ff_video_decoder.c b/src/libffmpeg/ff_video_decoder.c
index 3c69e360d..69a0feec1 100644
--- a/src/libffmpeg/ff_video_decoder.c
+++ b/src/libffmpeg/ff_video_decoder.c
@@ -1155,6 +1155,7 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) {
int offset = 0;
int codec_type = buf->type & 0xFFFF0000;
int video_step_to_use;
+ int frame_flags_to_use;
/* pad input data */
/* note: bitstream, alt bitstream reader or something will cause
@@ -1198,6 +1199,12 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) {
/* use externally provided video_step or fall back to stream's time_base otherwise */
video_step_to_use = (this->video_step || !this->context->time_base.den) ? this->video_step : (int)(90000ll * this->context->time_base.num / this->context->time_base.den);
+ frame_flags_to_use = this->frame_flags;
+
+ /* adjust frame flags for interlaced frames when running in demux_mpeg_pes context */
+ if (!this->video_step && this->av_frame->interlaced_frame)
+ frame_flags_to_use |= VO_INTERLACED_FLAG;
+
/* aspect ratio provided by ffmpeg, override previous setting */
if ((this->aspect_ratio_prio < 2) &&
av_cmp_q(this->context->sample_aspect_ratio, avr00)) {
@@ -1244,10 +1251,10 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) {
img = this->stream->video_out->get_frame (this->stream->video_out,
this->bih.biWidth,
- this->bih.biHeight,
+ (this->bih.biHeight + 31) & ~31,
this->aspect_ratio,
this->output_format,
- VO_BOTH_FIELDS|this->frame_flags);
+ VO_BOTH_FIELDS|frame_flags_to_use);
free_img = 1;
} else {
/* DR1 */
@@ -1265,10 +1272,10 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) {
/* DR1 */
img = this->stream->video_out->get_frame (this->stream->video_out,
img->width,
- img->height,
+ (img->height + 31) & ~31,
this->aspect_ratio,
this->output_format,
- VO_BOTH_FIELDS|this->frame_flags);
+ VO_BOTH_FIELDS|frame_flags_to_use);
free_img = 1;
}
@@ -1287,10 +1294,6 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) {
img->pts = this->pts;
this->pts = 0;
- /* workaround for demux_mpeg_pes sending fields as frames */
- if (!this->video_step && this->av_frame->interlaced_frame)
- video_step_to_use /= 2;
-
/* workaround for weird 120fps streams */
if( video_step_to_use == 750 ) {
/* fallback to the VIDEO_PTS_MODE */
@@ -1303,8 +1306,14 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) {
img->duration = video_step_to_use;
img->crop_right = this->crop_right;
- img->crop_bottom = this->crop_bottom;
-
+ img->crop_bottom = this->crop_bottom + (img->height - this->bih.biHeight);
+
+ /* transfer some more frame settings when running in demux_mpeg_pes context */
+ if (!this->video_step) {
+ img->progressive_frame = !this->av_frame->interlaced_frame;
+ img->top_field_first = this->av_frame->top_field_first;
+ }
+
this->skipframes = img->draw(img, this->stream);
if(free_img)
@@ -1312,19 +1321,27 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) {
}
}
- if (!got_one_picture) {
+ /* workaround for demux_mpeg_pes sending fields as frames:
+ * do not generate a bad frame for the first field picture
+ */
+ if (!got_one_picture && (this->size || this->video_step || !this->av_frame->interlaced_frame)) {
/* skipped frame, output a bad frame (of size 1x1 when size still uninitialized) */
img = this->stream->video_out->get_frame (this->stream->video_out,
- (this->bih.biWidth <= 0) ? 1 : this->bih.biWidth,
- (this->bih.biHeight <= 0) ? 1 : this->bih.biHeight,
+ (this->bih.biWidth <= 0) ? 16 : this->bih.biWidth,
+ (this->bih.biHeight <= 0) ? 32 : this->bih.biHeight,
this->aspect_ratio,
this->output_format,
- VO_BOTH_FIELDS|this->frame_flags);
+ VO_BOTH_FIELDS|frame_flags_to_use);
/* set PTS to allow early syncing */
img->pts = this->pts;
this->pts = 0;
img->duration = video_step_to_use;
+ /* transfer some more frame settings when running in demux_mpeg_pes context */
+ if (!this->video_step) {
+ img->progressive_frame = !this->av_frame->interlaced_frame;
+ img->top_field_first = this->av_frame->top_field_first;
+ }
img->bad_frame = 1;
this->skipframes = img->draw(img, this->stream);
img->free(img);