summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDarren Salt <devspam@moreofthesa.me.uk>2013-09-04 16:25:53 +0100
committerDarren Salt <devspam@moreofthesa.me.uk>2013-09-04 16:25:53 +0100
commitb196a6012ace0ce8636412cf3a439df1a170e797 (patch)
treea9eafe15ffddc480ed4fbaf8a47046e31e7447b7
parent7e1e415976f2feeed1f055baf64dcf574488dc66 (diff)
parentd08771bdead811e8ccebe6ec6d93a4ededd10300 (diff)
downloadxine-lib-b196a6012ace0ce8636412cf3a439df1a170e797.tar.gz
xine-lib-b196a6012ace0ce8636412cf3a439df1a170e797.tar.bz2
Merge.
-rw-r--r--AUTHORS7
-rw-r--r--doc/README.dvb15
-rw-r--r--src/combined/ffmpeg/ff_audio_decoder.c646
-rw-r--r--src/combined/ffmpeg/ff_video_decoder.c121
-rw-r--r--src/combined/ffmpeg/ffmpeg_compat.h4
-rw-r--r--src/combined/ffmpeg/xine_video.list2
-rw-r--r--src/demuxers/demux_flv.c1283
-rw-r--r--src/video_out/video_out_opengl2.c5
-rw-r--r--src/xine-engine/video_out.c29
9 files changed, 1323 insertions, 789 deletions
diff --git a/AUTHORS b/AUTHORS
index 982eca410..1d7bec5ba 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -578,4 +578,11 @@ Christoph Pfister <christophpfister@gmail.com>
Albert Lee <trisk+xine@acm.jhu.edu>
Solaris portability fixes (and other miscellaneous fixes)
+Christophe Thommeret <hftom@free.fr>
+ OpenGL 2.0 video out plugin, new VDPAU h264 decoder,
+ VDPAU bugfixes
+
+Edgar Hucek <gimli|@dark-green.com>
+ VAAPI Support
+
(let us know if we've forgotten anyone)
diff --git a/doc/README.dvb b/doc/README.dvb
index 21e2a41db..3feab71bd 100644
--- a/doc/README.dvb
+++ b/doc/README.dvb
@@ -133,5 +133,20 @@ at any time by pressing MENU4 (F3 in xine-ui, F4 in gxine). An OSD in the
top left hand corner will notify you of the status of the recording.
Pressing MENU4 again will resume recording.
+
+Using the Kaffeine DVB frontend
+-------------------------------
+
+Kaffeine has a very nice DVB (digital TV) frontend. However,
+since Qt/KDE 4, Kaffeine no longer has a built-in configuration GUI.
+The defaults are not really optimal. A suggested quick solution is:
+ * close Kaffeine
+ * build/install xine-ui
+ * do your configuration with that
+ * make Kaffeine use it too:
+ $ ln -sf ~/.xine/config ~/.kde4/share/apps/kaffeine/xine-config
+A similar trick applies to previewing videos in Gwenview via phonon:
+ $ ln -sf ~/.xine/config ~/.config/kde.org/Phonon-Xine.xine.conf
+
Have fun.
diff --git a/src/combined/ffmpeg/ff_audio_decoder.c b/src/combined/ffmpeg/ff_audio_decoder.c
index 205a64370..1a80b9dcf 100644
--- a/src/combined/ffmpeg/ff_audio_decoder.c
+++ b/src/combined/ffmpeg/ff_audio_decoder.c
@@ -46,6 +46,8 @@
#define AUDIOBUFSIZE (64 * 1024)
+#define MAX_CHANNELS 6
+
typedef struct {
audio_decoder_class_t decoder_class;
@@ -59,11 +61,6 @@ typedef struct ff_audio_decoder_s {
xine_stream_t *stream;
- int output_open;
- int audio_channels;
- int audio_bits;
- int audio_sample_rate;
-
unsigned char *buf;
int bufsize;
int size;
@@ -78,6 +75,29 @@ typedef struct ff_audio_decoder_s {
#if AVAUDIO > 3
AVFrame *av_frame;
#endif
+
+ /* decoder settings */
+ int ff_channels;
+ int ff_bits;
+ int ff_sample_rate;
+ int64_t ff_map;
+
+ /* channel mixer settings */
+ /* map[ao_channel] = ff_channel */
+ int8_t map[MAX_CHANNELS];
+ int8_t left[4], right[4];
+ /* how many left[] / right[] entries are in use */
+ int front_mixes;
+ /* volume adjustment */
+ int downmix_shift;
+
+ /* audio out settings */
+ int output_open;
+ int ao_channels;
+ int new_mode;
+ int ao_mode;
+ int ao_caps;
+
} ff_audio_decoder_t;
@@ -163,11 +183,11 @@ static void ff_audio_init_codec(ff_audio_decoder_t *this, unsigned int codec_typ
/* Current ffmpeg audio decoders usually use 16 bits/sample
* buf->decoder_info[2] can't be used as it doesn't refer to the output
* bits/sample for some codecs (e.g. MS ADPCM) */
- this->audio_bits = 16;
+ this->ff_bits = 16;
- this->context->bits_per_sample = this->audio_bits;
- this->context->sample_rate = this->audio_sample_rate;
- this->context->channels = this->audio_channels;
+ this->context->bits_per_sample = this->ff_bits;
+ this->context->sample_rate = this->ff_sample_rate;
+ this->context->channels = this->ff_channels;
this->context->codec_id = this->codec->id;
this->context->codec_type = this->codec->type;
this->context->codec_tag = _x_stream_info_get(this->stream, XINE_STREAM_INFO_AUDIO_FOURCC);
@@ -237,8 +257,8 @@ static void ff_handle_header_buffer(ff_audio_decoder_t *this, buf_element_t *buf
}
if(buf->decoder_flags & BUF_FLAG_STDHEADER) {
- this->audio_sample_rate = buf->decoder_info[1];
- this->audio_channels = buf->decoder_info[3];
+ this->ff_sample_rate = buf->decoder_info[1];
+ this->ff_channels = buf->decoder_info[3];
if(this->size) {
audio_header = (xine_waveformatex *)this->buf;
@@ -259,15 +279,15 @@ static void ff_handle_header_buffer(ff_audio_decoder_t *this, buf_element_t *buf
switch(codec_type) {
case BUF_AUDIO_14_4:
- this->audio_sample_rate = 8000;
- this->audio_channels = 1;
+ this->ff_sample_rate = 8000;
+ this->ff_channels = 1;
this->context->block_align = 240;
break;
case BUF_AUDIO_28_8:
- this->audio_sample_rate = _X_BE_16(&this->buf[0x30]);
- this->audio_channels = this->buf[0x37];
- /* this->audio_bits = buf->content[0x35] */
+ this->ff_sample_rate = _X_BE_16(&this->buf[0x30]);
+ this->ff_channels = this->buf[0x37];
+ /* this->ff_bits = buf->content[0x35] */
this->context->block_align = _X_BE_32(&this->buf[0x18]);
@@ -284,7 +304,7 @@ static void ff_handle_header_buffer(ff_audio_decoder_t *this, buf_element_t *buf
xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
"ffmpeg_audio_dec: 28_8 audio channels %d bits %d sample rate %d block align %d\n",
- this->audio_channels, this->audio_bits, this->audio_sample_rate,
+ this->ff_channels, this->ff_bits, this->ff_sample_rate,
this->context->block_align);
break;
case BUF_AUDIO_COOK:
@@ -295,15 +315,15 @@ static void ff_handle_header_buffer(ff_audio_decoder_t *this, buf_element_t *buf
version = _X_BE_16 (this->buf+4);
if (version == 4) {
- this->audio_sample_rate = _X_BE_16 (this->buf+48);
- this->audio_bits = _X_BE_16 (this->buf+52);
- this->audio_channels = _X_BE_16 (this->buf+54);
+ this->ff_sample_rate = _X_BE_16 (this->buf+48);
+ this->ff_bits = _X_BE_16 (this->buf+52);
+ this->ff_channels = _X_BE_16 (this->buf+54);
data_len = _X_BE_32 (this->buf+67);
extradata = 71;
} else {
- this->audio_sample_rate = _X_BE_16 (this->buf+54);
- this->audio_bits = _X_BE_16 (this->buf+58);
- this->audio_channels = _X_BE_16 (this->buf+60);
+ this->ff_sample_rate = _X_BE_16 (this->buf+54);
+ this->ff_bits = _X_BE_16 (this->buf+58);
+ this->ff_channels = _X_BE_16 (this->buf+60);
data_len = _X_BE_32 (this->buf+74);
extradata = 78;
}
@@ -311,7 +331,7 @@ static void ff_handle_header_buffer(ff_audio_decoder_t *this, buf_element_t *buf
xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
"ffmpeg_audio_dec: cook audio channels %d bits %d sample rate %d block align %d\n",
- this->audio_channels, this->audio_bits, this->audio_sample_rate,
+ this->ff_channels, this->ff_bits, this->ff_sample_rate,
this->context->block_align);
if (extradata + data_len > this->size)
@@ -363,11 +383,146 @@ static void ff_audio_output_close(ff_audio_decoder_t *this)
this->output_open = 0;
}
- this->audio_bits = 0;
- this->audio_sample_rate = 0;
- this->audio_channels = 0;
+ this->ff_sample_rate = 0;
+ this->ao_mode = 0;
}
+static void ff_map_channels (ff_audio_decoder_t *this) {
+ uint64_t ff_map;
+ int caps = this->stream->audio_out->get_capabilities (this->stream->audio_out);
+
+ /* safety kludge for very old libavcodec */
+#ifdef AV_CH_FRONT_LEFT
+ ff_map = this->context->channel_layout;
+ if (!ff_map) /* wma2 bug */
+#endif
+ ff_map = (1 << this->context->channels) - 1;
+
+ if ((caps != this->ao_caps) || (ff_map != this->ff_map)) {
+ int i, j;
+ /* ff: see names[] below; xine: L R RL RR C LFE */
+ const int8_t base_map[] = {0, 1, 4, 5, 2, 3, -1, -1, -1, 2, 3};
+ int8_t name_map[MAX_CHANNELS];
+ const int modes[] = {
+ AO_CAP_MODE_MONO, AO_CAP_MODE_STEREO,
+ AO_CAP_MODE_4CHANNEL, AO_CAP_MODE_4_1CHANNEL,
+ AO_CAP_MODE_5CHANNEL, AO_CAP_MODE_5_1CHANNEL
+ };
+ const int num_modes = sizeof (modes) / sizeof (modes[0]);
+ const int8_t mode_channels[] = {1, 2, 4, 6, 6, 6};
+ const int8_t wishlist[] = {
+ 0, 1, 2, 3, 4, 5, /* mono */
+ 1, 2, 3, 4, 5, 0, /* stereo */
+ 5, 4, 3, 2, 1, 0, /* center + lfe */
+ 4, 5, 2, 3, 1, 0, /* center */
+ 3, 5, 2, 4, 1, 0, /* lfe */
+ 2, 3, 4, 5, 1, 0 /* 4.0 */
+ };
+ const int8_t *tries;
+
+ this->ao_caps = caps;
+ this->ff_map = ff_map;
+ this->ff_channels = this->context->channels;
+
+ /* silence out */
+ for (i = 0; i < MAX_CHANNELS; i++)
+ this->map[i] = -1;
+ for (i = 0; i < 4; i++)
+ this->left[i] = this->right[i] = -1;
+
+ /* set up raw map and ao mode wishlist */
+ if (this->ff_channels == 1) { /* mono */
+ name_map[0] = 2;
+ this->left[0] = this->right[0] = 0;
+ tries = wishlist + 0 * num_modes;
+ } else if (this->ff_channels == 2) { /* stereo */
+ name_map[0] = 0;
+ name_map[1] = 1;
+ this->left[0] = 0;
+ this->right[0] = 1;
+ tries = wishlist + 1 * num_modes;
+ } else {
+ for (i = j = 0; i < sizeof (base_map) / sizeof (base_map[0]); i++) {
+ if ((ff_map >> i) & 1) {
+ int8_t target = base_map[i];
+ if ((target >= 0) && (this->map[target] < 0))
+ this->map[target] = j;
+ name_map[j] = i; /* for debug output below */
+ j++;
+ }
+ }
+ this->left[0] = this->map[0] < 0 ? 0 : this->map[0];
+ this->map[0] = -1;
+ this->right[0] = this->map[1] < 0 ? 1 : this->map[1];
+ this->map[1] = -1;
+ tries = wishlist
+ + (2 + (this->map[4] < 0 ? 2 : 0) + (this->map[5] < 0 ? 1 : 0)) * num_modes;
+ }
+ this->front_mixes = 1;
+
+ /* find ao mode */
+ for (i = 0; i < num_modes; i++) if (caps & modes[tries[i]]) break;
+ i = i == num_modes ? 1 : tries[i];
+ this->new_mode = modes[i];
+ this->ao_channels = mode_channels[i];
+
+ /* mix center to front */
+ if ((this->map[4] >= 0) && !((0x30 >> i) & 1)) {
+ this->left[this->front_mixes] = this->map[4];
+ this->right[this->front_mixes++] = this->map[4];
+ this->map[4] = -1;
+ }
+ /* mix lfe to front */
+ if ((this->map[5] >= 0) && !((0x28 >> i) & 1)) {
+ this->left[this->front_mixes] = this->map[5];
+ this->right[this->front_mixes++] = this->map[5];
+ this->map[5] = -1;
+ }
+ /* mix surround to front */
+ if ((this->map[2] >= 0) && (this->map[3] >= 0) && !((0x3c >> i) & 1)) {
+ this->left[this->front_mixes] = this->map[2];
+ this->right[this->front_mixes++] = this->map[3];
+ this->map[2] = -1;
+ this->map[3] = -1;
+ }
+
+ this->downmix_shift = this->front_mixes > 1 ? 1 : 0;
+ /* this will be on the safe side but usually too soft?? */
+#if 0
+ if (this->front_mixes > 2)
+ this->downmix_shift = 2;
+#endif
+
+ if (this->stream->xine->verbosity >= XINE_VERBOSITY_LOG) {
+ const int8_t *names[] = {
+ "left", "right", "center", "bass",
+ "rear left", "rear right",
+ "half left", "half right",
+ "rear center",
+ "side left", "side right"
+ };
+ int8_t buf[200];
+ int p = sprintf (buf, "ff_audio_dec: channel layout: ");
+ int8_t *indx = this->left;
+ for (i = 0; i < 2; i++) {
+ buf[p++] = '[';
+ for (j = 0; j < this->front_mixes; j++)
+ p += sprintf (buf + p, "%s%s", names[name_map[indx[j]]], (j < this->front_mixes - 1) ? " + " : "");
+ buf[p++] = ']';
+ buf[p++] = ' ';
+ indx = this->right;
+ }
+ for (i = 2; i < this->ao_channels; i++)
+ p += sprintf (buf + p, "[%s] ",
+ ((this->map[i] < 0) || (this->map[i] > 5)) ? (const int8_t *)"-" : names[name_map[this->map[i]]]);
+ buf[p++] = '\n';
+ fwrite (buf, 1, p, stdout);
+ }
+ }
+}
+
+#define CLIP_16(v) ((v + 0x8000) & ~0xffff ? (v >> 31) ^ 0x7fff : v)
+
static int ff_audio_decode (ff_audio_decoder_t *this,
int16_t *decode_buffer, int *decode_buffer_size, uint8_t *buf, int size) {
int consumed;
@@ -408,106 +563,250 @@ static int ff_audio_decode (ff_audio_decoder_t *this,
avpkt.flags = AV_PKT_FLAG_KEY;
# if AVAUDIO > 3
int got_frame;
- const float gain = this->class->gain;
+ float gain = this->class->gain;
if (!this->av_frame)
this->av_frame = avcodec_alloc_frame ();
consumed = avcodec_decode_audio4 (this->context, this->av_frame, &got_frame, &avpkt);
if ((consumed >= 0) && got_frame) {
+ /* setup may have altered while decoding */
+ ff_map_channels (this);
+
int16_t *q = decode_buffer;
int samples = this->av_frame->nb_samples;
- int channels = this->context->channels;
- int bytes, i, j;
+ int channels = this->ao_channels;
+ int bytes, i, j, shift = this->downmix_shift;
/* limit buffer */
- if (channels > 12)
- channels = 12;
if (*decode_buffer_size < samples * channels * 2)
samples = *decode_buffer_size / (channels * 2);
bytes = samples * channels * 2;
*decode_buffer_size = bytes;
- /* convert to packed int16_t. I guess there is something
- in libavfilter but also another dependency... */
+ /* TJ. convert to packed int16_t while respecting the user's speaker arrangement.
+ I tried to speed up and not to pull in libswresample. */
+ for (i = 2; i < channels; i++) if (this->map[i] < 0) {
+ /* clear if there is an upmix mute channel */
+ memset (q, 0, bytes);
+ break;
+ }
+ /* For mono output, downmix to stereo first */
+ if ((channels == 1) && (this->ff_channels > 1))
+ channels = 2;
+ gain /= (float)(1 << shift);
switch (this->context->sample_fmt) {
+#define MIX_AUDIO(stype,planar,idx,num,dindx) {\
+ stype *p1, *p2, *p3, *p4;\
+ int i, sstep;\
+ int8_t *x = idx;\
+ int16_t *dptr = (int16_t *)decode_buffer + dindx;\
+ if (planar) {\
+ p1 = (stype *)this->av_frame->extended_data[x[0]];\
+ sstep = 1;\
+ } else {\
+ p1 = (stype *)this->av_frame->extended_data[0] + x[0];\
+ sstep = this->ff_channels;\
+ }\
+ if (num == 1) {\
+ if (p1) for (i = 0; i < samples; i++) {\
+ int32_t v = MIX_FIX(*p1);\
+ p1 += sstep;\
+ v >>= shift;\
+ *dptr = (v);\
+ dptr += channels;\
+ }\
+ } else {\
+ if (planar)\
+ p2 = (stype *)this->av_frame->extended_data[x[1]];\
+ else\
+ p2 = (stype *)this->av_frame->extended_data[0] + x[1];\
+ if (num == 2) {\
+ if (p1 && p2) for (i = 0; i < samples; i++) {\
+ int32_t v = MIX_FIX(*p1);\
+ p1 += sstep;\
+ v += MIX_FIX(*p2);\
+ p2 += sstep;\
+ v >>= shift;\
+ *dptr = CLIP_16(v);\
+ dptr += channels;\
+ }\
+ } else {\
+ if (planar)\
+ p3 = (stype *)this->av_frame->extended_data[x[2]];\
+ else\
+ p3 = (stype *)this->av_frame->extended_data[0] + x[2];\
+ if (num == 3) {\
+ if (p1 && p2 && p3) for (i = 0; i < samples; i++) {\
+ int32_t v = MIX_FIX(*p1);\
+ p1 += sstep;\
+ v += MIX_FIX(*p2);\
+ p2 += sstep;\
+ v += MIX_FIX(*p3);\
+ p3 += sstep;\
+ v >>= shift;\
+ *dptr = CLIP_16(v);\
+ dptr += channels;\
+ }\
+ } else {\
+ if (planar)\
+ p4 = (stype *)this->av_frame->extended_data[x[3]];\
+ else\
+ p4 = (stype *)this->av_frame->extended_data[0] + x[3];\
+ if (p1 && p2 && p3 && p4) for (i = 0; i < samples; i++) {\
+ int32_t v = MIX_FIX(*p1);\
+ p1 += sstep;\
+ v += MIX_FIX(*p2);\
+ p2 += sstep;\
+ v += MIX_FIX(*p3);\
+ p3 += sstep;\
+ v += MIX_FIX(*p4);\
+ p4 += sstep;\
+ v >>= shift;\
+ *dptr = CLIP_16(v);\
+ dptr += channels;\
+ }\
+ }\
+ }\
+ }\
+ }
+#define MIX_FIX(v) (((int16_t)(v)<<8)^0x8000)
case AV_SAMPLE_FMT_U8P:
- if (channels > 1) {
- uint8_t *p[12];
- for (i = 0; i < channels; i++)
- p[i] = (uint8_t *)this->av_frame->extended_data[i];
- for (i = samples; i; i--) {
- for (j = 0; j < channels; j++)
- *q++ = ((uint16_t)(*p[j]++) << 8) ^ 0x8000;
- }
- break;
- }
+ MIX_AUDIO (uint8_t, 1, this->left, this->front_mixes, 0);
+ MIX_AUDIO (uint8_t, 1, this->right, this->front_mixes, 1);
+ for (j = 0; j < channels; j++) if (this->map[j] >= 0)
+ MIX_AUDIO (uint8_t, 1, this->map + j, 1, j);
+ break;
case AV_SAMPLE_FMT_U8:
- {
- uint8_t *p = (uint8_t *)this->av_frame->extended_data[0];
- for (i = samples * channels; i; i--)
- *q++ = ((uint16_t)(*p++) << 8) ^ 0x8000;
- }
+ MIX_AUDIO (uint8_t, 0, this->left, this->front_mixes, 0);
+ MIX_AUDIO (uint8_t, 0, this->right, this->front_mixes, 1);
+ for (j = 0; j < channels; j++) if (this->map[j] >= 0)
+ MIX_AUDIO (uint8_t, 0, this->map + j, 1, j);
break;
+#undef MIX_FIX
+#define MIX_FIX(v) (v)
case AV_SAMPLE_FMT_S16P:
- if (channels > 1) {
- int16_t *p[12];
- for (i = 0; i < channels; i++)
- p[i] = (int16_t *)this->av_frame->extended_data[i];
- for (i = samples; i; i--) {
- for (j = 0; j < channels; j++)
- *q++ = *p[j]++;
- }
- break;
- }
+ MIX_AUDIO (int16_t, 1, this->left, this->front_mixes, 0);
+ MIX_AUDIO (int16_t, 1, this->right, this->front_mixes, 1);
+ for (j = 0; j < channels; j++) if (this->map[j] >= 0)
+ MIX_AUDIO (int16_t, 1, this->map + j, 1, j);
+ break;
case AV_SAMPLE_FMT_S16:
- xine_fast_memcpy (q, this->av_frame->extended_data[0], bytes);
+ MIX_AUDIO (int16_t, 0, this->left, this->front_mixes, 0);
+ MIX_AUDIO (int16_t, 0, this->right, this->front_mixes, 1);
+ for (j = 0; j < channels; j++) if (this->map[j] >= 0)
+ MIX_AUDIO (int16_t, 0, this->map + j, 1, j);
break;
+#undef MIX_FIX
+#define MIX_FIX(v) ((v)>>16)
case AV_SAMPLE_FMT_S32P:
- if (channels > 1) {
- int32_t *p[12];
- for (i = 0; i < channels; i++)
- p[i] = (int32_t *)this->av_frame->extended_data[i];
- for (i = samples; i; i--) {
- for (j = 0; j < channels; j++)
- *q++ = *p[j]++ >> 16;
- }
- break;
- }
+ MIX_AUDIO (int32_t, 1, this->left, this->front_mixes, 0);
+ MIX_AUDIO (int32_t, 1, this->right, this->front_mixes, 1);
+ for (j = 0; j < channels; j++) if (this->map[j] >= 0)
+ MIX_AUDIO (int32_t, 1, this->map + j, 1, j);
+ break;
case AV_SAMPLE_FMT_S32:
- {
- int32_t *p = (int32_t *)this->av_frame->extended_data[0];
- for (i = samples * channels; i; i--)
- *q++ = *p++ >> 16;
- }
+ MIX_AUDIO (int32_t, 0, this->left, this->front_mixes, 0);
+ MIX_AUDIO (int32_t, 0, this->right, this->front_mixes, 1);
+ for (j = 0; j < channels; j++) if (this->map[j] >= 0)
+ MIX_AUDIO (int32_t, 0, this->map + j, 1, j);
break;
+#undef MIX_FIX
+#undef MIX_AUDIO
+#define MIX_AUDIO(stype,planar,idx,num,dindx) {\
+ stype *p1, *p2, *p3, *p4;\
+ int i, sstep;\
+ int8_t *x = idx;\
+ int16_t *dptr = (int16_t *)decode_buffer + dindx;\
+ if (planar) {\
+ p1 = (stype *)this->av_frame->extended_data[x[0]];\
+ sstep = 1;\
+ } else {\
+ p1 = (stype *)this->av_frame->extended_data[0] + x[0];\
+ sstep = this->ff_channels;\
+ }\
+ if (num == 1) {\
+ if (p1) for (i = 0; i < samples; i++) {\
+ int32_t v = (*p1) * gain;\
+ p1 += sstep;\
+ *dptr = CLIP_16(v);\
+ dptr += channels;\
+ }\
+ } else {\
+ if (planar)\
+ p2 = (stype *)this->av_frame->extended_data[x[1]];\
+ else\
+ p2 = (stype *)this->av_frame->extended_data[0] + x[1];\
+ if (num == 2) {\
+ if (p1 && p2) for (i = 0; i < samples; i++) {\
+ int32_t v = (*p1 + *p2) * gain;\
+ p1 += sstep;\
+ p2 += sstep;\
+ *dptr = CLIP_16(v);\
+ dptr += channels;\
+ }\
+ } else {\
+ if (planar)\
+ p3 = (stype *)this->av_frame->extended_data[x[2]];\
+ else\
+ p3 = (stype *)this->av_frame->extended_data[0] + x[2];\
+ if (num == 3) {\
+ if (p1 && p2 && p3) for (i = 0; i < samples; i++) {\
+ int32_t v = (*p1 + *p2 + *p3) * gain;\
+ p1 += sstep;\
+ p2 += sstep;\
+ p3 += sstep;\
+ *dptr = CLIP_16(v);\
+ dptr += channels;\
+ }\
+ } else {\
+ if (planar)\
+ p4 = (stype *)this->av_frame->extended_data[x[3]];\
+ else\
+ p4 = (stype *)this->av_frame->extended_data[0] + x[3];\
+ if (p1 && p2 && p3 && p4) for (i = 0; i < samples; i++) {\
+ int32_t v = (*p1 + *p2 + *p3 + *p4) * gain;\
+ p1 += sstep;\
+ p2 += sstep;\
+ p3 += sstep;\
+ p4 += sstep;\
+ *dptr = CLIP_16(v);\
+ dptr += channels;\
+ }\
+ }\
+ }\
+ }\
+ }
case AV_SAMPLE_FMT_FLTP: /* the most popular one */
- if (channels > 1) {
- float *p[12];
- for (i = 0; i < channels; i++)
- p[i] = (float *)this->av_frame->extended_data[i];
- for (i = samples; i; i--) {
- for (j = 0; j < channels; j++) {
- int v = *p[j]++ * gain;
- *q++ = (v + 0x8000) & ~0xffff ? (v >> 31) ^ 0x7fff : v;
- }
- }
- break;
- }
+ MIX_AUDIO (float, 1, this->left, this->front_mixes, 0);
+ MIX_AUDIO (float, 1, this->right, this->front_mixes, 1);
+ for (j = 0; j < channels; j++) if (this->map[j] >= 0)
+ MIX_AUDIO (float, 1, this->map + j, 1, j);
+ break;
case AV_SAMPLE_FMT_FLT:
- {
- float *p = (float *)this->av_frame->extended_data[0];
- for (i = samples * channels; i; i--) {
- int v = *p++ * gain;
- *q++ = (v + 0x8000) & ~0xffff ? (v >> 31) ^ 0x7fff : v;
- }
- }
+ MIX_AUDIO (float, 0, this->left, this->front_mixes, 0);
+ MIX_AUDIO (float, 0, this->right, this->front_mixes, 1);
+ for (j = 0; j < channels; j++) if (this->map[j] >= 0)
+ MIX_AUDIO (float, 0, this->map + j, 1, j);
break;
default: ;
}
+ if (channels > this->ao_channels) {
+ /* final mono downmix */
+ int16_t *p = decode_buffer;
+ q = p;
+ for (i = samples; i; i--) {
+ int v = *p++;
+ v += *p++;
+ *q++ = v >> 1;
+ }
+ *decode_buffer_size = samples * 2;
+ }
} else *decode_buffer_size = 0;
# else
consumed = avcodec_decode_audio3 (this->context, decode_buffer, decode_buffer_size, &avpkt);
+ ff_map_channels (this);
# endif
#else
consumed = avcodec_decode_audio2 (this->context, decode_buffer, decode_buffer_size, buf, size);
+ ff_map_channels (this);
#endif
if (consumed < 0) {
@@ -567,23 +866,25 @@ static void ff_audio_decode_data (audio_decoder_t *this_gen, buf_element_t *buf)
while (this->size>=0) {
decode_buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
- bytes_consumed =
- ff_audio_decode(this,
- (int16_t *)this->decode_buffer, &decode_buffer_size,
- &this->buf[offset], this->size);
+ bytes_consumed = ff_audio_decode (this, (int16_t *)this->decode_buffer, &decode_buffer_size,
+ &this->buf[offset], this->size);
- if (bytes_consumed<0) {
- this->size=0;
- return;
- } else if (bytes_consumed == 0 && decode_buffer_size == 0) {
+ if (bytes_consumed < 0)
+ break;
+
+ offset += bytes_consumed;
+ this->size -= bytes_consumed;
+
+ if (decode_buffer_size == 0) {
+ if (bytes_consumed)
+ continue;
if (offset)
memmove(this->buf, &this->buf[offset], this->size);
return;
}
- if (this->audio_bits != this->context->bits_per_sample ||
- this->audio_sample_rate != this->context->sample_rate ||
- this->audio_channels != this->context->channels) {
+ if (this->ff_sample_rate != this->context->sample_rate ||
+ this->ao_mode != this->new_mode) {
xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
_("ffmpeg_audio_dec: codec parameters changed\n"));
/* close if it was open, and always trigger 1 new open attempt below */
@@ -591,12 +892,11 @@ static void ff_audio_decode_data (audio_decoder_t *this_gen, buf_element_t *buf)
}
if (!this->output_open) {
- if (!this->audio_bits || !this->audio_sample_rate || !this->audio_channels) {
- this->audio_bits = this->context->bits_per_sample;
- this->audio_sample_rate = this->context->sample_rate;
- this->audio_channels = this->context->channels;
+ if (!this->ff_sample_rate || !this->ao_mode) {
+ this->ff_sample_rate = this->context->sample_rate;
+ this->ao_mode = this->new_mode;
}
- if (!this->audio_bits || !this->audio_sample_rate || !this->audio_channels) {
+ if (!this->ff_sample_rate || !this->new_mode) {
xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
_("ffmpeg_audio_dec: cannot read codec parameters from packet\n"));
/* try to decode next packet. */
@@ -606,8 +906,8 @@ static void ff_audio_decode_data (audio_decoder_t *this_gen, buf_element_t *buf)
buf->pts = 0;
} else {
this->output_open = (this->stream->audio_out->open) (this->stream->audio_out,
- this->stream, this->audio_bits, this->audio_sample_rate,
- _x_ao_channels2mode(this->audio_channels));
+ this->stream, 16, this->ff_sample_rate,
+ this->ao_mode);
if (!this->output_open) {
xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
"ffmpeg_audio_dec: error opening audio output\n");
@@ -617,6 +917,84 @@ static void ff_audio_decode_data (audio_decoder_t *this_gen, buf_element_t *buf)
}
}
+#if AVAUDIO < 4
+ /* Old style postprocessing */
+ if (codec_type == BUF_AUDIO_WMAPRO) {
+ /* the above codecs output float samples, not 16-bit integers */
+ int samples = decode_buffer_size / sizeof(float);
+ float gain = this->class->gain;
+ float *p = (float *)this->decode_buffer;
+ int16_t *q = (int16_t *)this->decode_buffer;
+ int i;
+ for (i = samples; i; i--) {
+ int v = *p++ * gain;
+ *q++ = CLIP_16 (v);
+ }
+ decode_buffer_size = samples * 2;
+ }
+
+ if ((this->ao_channels != this->ff_channels) || (this->ao_channels > 2)) {
+ /* Channel reordering and/or mixing */
+ int samples = decode_buffer_size / (this->ff_channels * 2);
+ int channels = this->ao_channels;
+ int ff_channels = this->ff_channels;
+ int16_t *p = (int16_t *)this->decode_buffer;
+ int16_t *q = p;
+ int shift = this->downmix_shift, i, j;
+ /* downmix mono output to stereo first */
+ if ((channels == 1) && (ff_channels > 1))
+ channels = 2;
+ /* move to end of buf for in-place editing */
+ p += AVCODEC_MAX_AUDIO_FRAME_SIZE - decode_buffer_size;
+ if (p >= q + decode_buffer_size)
+ xine_fast_memcpy (p, q, decode_buffer_size);
+ else
+ memmove (p, q, decode_buffer_size);
+ /* not very optimized but it only hits when playing multichannel audio through
+ old ffmpeg - and its still better than previous code there */
+ if (this->front_mixes < 2) {
+ /* just reorder and maybe upmix */
+ for (i = samples; i; i--) {
+ q[0] = p[0];
+ q[1] = p[this->right[0]];
+ for (j = 2; j < channels; j++)
+ q[j] = this->map[j] < 0 ? 0 : p[this->map[j]];
+ p += ff_channels;
+ q += channels;
+ }
+ } else {
+ /* downmix */
+ for (i = samples; i; i--) {
+ int left = p[0];
+ int right = p[this->right[0]];
+ for (j = 1; j < this->front_mixes; j++) {
+ left += p[this->left[j]];
+ right += p[this->right[j]];
+ }
+ left >>= shift;
+ q[0] = CLIP_16 (left);
+ right >>= shift;
+ q[1] = CLIP_16 (right);
+ for (j = 2; j < channels; j++)
+ q[j] = this->map[j] < 0 ? 0 : p[this->map[j]] >> shift;
+ p += ff_channels;
+ q += channels;
+ }
+ }
+ /* final mono downmix */
+ if (channels > this->ao_channels) {
+ p = (int16_t *)this->decode_buffer;
+ q = p;
+ for (i = samples; i; i--) {
+ int v = *p++;
+ v += *p++;
+ *q++ = v >> 1;
+ }
+ }
+ decode_buffer_size = samples * this->ao_channels * 2;
+ }
+#endif
+
/* dispatch the decoded audio */
out = 0;
while (out < decode_buffer_size) {
@@ -636,40 +1014,16 @@ static void ff_audio_decode_data (audio_decoder_t *this_gen, buf_element_t *buf)
}
/* fill up this buffer */
-#if AVAUDIO < 4
- if (codec_type == BUF_AUDIO_WMAPRO) {
- /* the above codecs output float samples, not 16-bit integers */
- int bytes_per_sample = sizeof(float);
- if (((decode_buffer_size - out) * 2 / bytes_per_sample) > audio_buffer->mem_size)
- bytes_to_send = audio_buffer->mem_size * bytes_per_sample / 2;
- else
- bytes_to_send = decode_buffer_size - out;
-
- int16_t *int_buffer = calloc(1, bytes_to_send * 2 / bytes_per_sample);
- int i;
- for (i = 0; i < (bytes_to_send / bytes_per_sample); i++) {
- float *float_sample = (float *)&this->decode_buffer[i * bytes_per_sample + out];
- int_buffer[i] = (int16_t)lrintf(*float_sample * 32768.);
- }
+ if ((decode_buffer_size - out) > audio_buffer->mem_size)
+ bytes_to_send = audio_buffer->mem_size;
+ else
+ bytes_to_send = decode_buffer_size - out;
- out += bytes_to_send;
- bytes_to_send = bytes_to_send * 2 / bytes_per_sample;
- xine_fast_memcpy(audio_buffer->mem, int_buffer, bytes_to_send);
- free(int_buffer);
- } else
-#endif
- {
- if ((decode_buffer_size - out) > audio_buffer->mem_size)
- bytes_to_send = audio_buffer->mem_size;
- else
- bytes_to_send = decode_buffer_size - out;
-
- xine_fast_memcpy(audio_buffer->mem, &this->decode_buffer[out], bytes_to_send);
- out += bytes_to_send;
- }
+ xine_fast_memcpy(audio_buffer->mem, &this->decode_buffer[out], bytes_to_send);
+ out += bytes_to_send;
/* byte count / 2 (bytes / sample) / channels */
- audio_buffer->num_frames = bytes_to_send / 2 / this->audio_channels;
+ audio_buffer->num_frames = bytes_to_send / 2 / this->ao_channels;
audio_buffer->vpts = buf->pts;
@@ -677,11 +1031,7 @@ static void ff_audio_decode_data (audio_decoder_t *this_gen, buf_element_t *buf)
this->stream->audio_out->put_buffer (this->stream->audio_out,
audio_buffer, this->stream);
}
-
- this->size -= bytes_consumed;
- offset += bytes_consumed;
}
-
/* reset internal accumulation buffer */
this->size = 0;
}
@@ -765,7 +1115,7 @@ static audio_decoder_t *ff_audio_open_plugin (audio_decoder_class_t *class_gen,
this->audio_decoder.dispose = ff_audio_dispose;
this->output_open = 0;
- this->audio_channels = 0;
+ this->ff_channels = 0;
this->stream = stream;
this->buf = NULL;
this->size = 0;
diff --git a/src/combined/ffmpeg/ff_video_decoder.c b/src/combined/ffmpeg/ff_video_decoder.c
index 94a0d251d..b24f108f4 100644
--- a/src/combined/ffmpeg/ff_video_decoder.c
+++ b/src/combined/ffmpeg/ff_video_decoder.c
@@ -131,7 +131,7 @@ struct ff_video_decoder_s {
double aspect_ratio;
int aspect_ratio_prio;
int frame_flags;
- int crop_right, crop_bottom;
+ int edge;
int output_format;
@@ -157,6 +157,8 @@ struct ff_video_decoder_s {
uint8_t set_stream_info;
#ifdef ENABLE_VAAPI
+ int vaapi_width, vaapi_height;
+ int vaapi_profile;
struct vaapi_context vaapi_context;
vaapi_accel_t *accel;
vo_frame_t *accel_img;
@@ -225,6 +227,7 @@ static int get_buffer(AVCodecContext *context, AVFrame *av_frame){
vo_frame_t *img;
int width = context->width;
int height = context->height;
+ int crop_right = 0, crop_bottom = 0;
int guarded_render = 0;
ff_check_colorspace (this);
@@ -232,13 +235,13 @@ static int get_buffer(AVCodecContext *context, AVFrame *av_frame){
if (!this->bih.biWidth || !this->bih.biHeight) {
this->bih.biWidth = width;
this->bih.biHeight = height;
+ }
- if (this->aspect_ratio_prio == 0) {
- this->aspect_ratio = (double)width / (double)height;
- this->aspect_ratio_prio = 1;
- lprintf("default aspect ratio: %f\n", this->aspect_ratio);
- this->set_stream_info = 1;
- }
+ if (this->aspect_ratio_prio == 0) {
+ this->aspect_ratio = (double)width / (double)height;
+ this->aspect_ratio_prio = 1;
+ lprintf("default aspect ratio: %f\n", this->aspect_ratio);
+ this->set_stream_info = 1;
}
avcodec_align_dimensions(context, &width, &height);
@@ -257,6 +260,26 @@ static int get_buffer(AVCodecContext *context, AVFrame *av_frame){
#endif
av_frame->reordered_opaque = context->reordered_opaque;
+ /* reinitialize vaapi for new image size */
+ if (context->width != this->vaapi_width || context->height != this->vaapi_height) {
+ VAStatus status;
+
+ this->vaapi_width = context->width;
+ this->vaapi_height = context->height;
+ status = this->accel->vaapi_init (this->accel_img, this->vaapi_profile,
+ context->width, context->height, 0);
+
+ if (status == VA_STATUS_SUCCESS) {
+ ff_vaapi_context_t *va_context = this->accel->get_context (this->accel_img);
+
+ if (va_context) {
+ this->vaapi_context.config_id = va_context->va_config_id;
+ this->vaapi_context.context_id = va_context->va_context_id;
+ this->vaapi_context.display = va_context->va_display;
+ }
+ }
+ }
+
if(!this->accel->guarded_render(this->accel_img)) {
img = this->stream->video_out->get_frame (this->stream->video_out,
width,
@@ -305,6 +328,12 @@ static int get_buffer(AVCodecContext *context, AVFrame *av_frame){
guarded_render = this->accel->guarded_render(this->accel_img);
#endif /* ENABLE_VAAPI */
+ /* The alignment rhapsody */
+ width += 2 * this->edge;
+ height += 2 * this->edge;
+ width = (width + 15) & ~15;
+ height = (height + 15) & ~15;
+
if ((this->full2mpeg || (this->context->pix_fmt != PIX_FMT_YUV420P &&
this->context->pix_fmt != PIX_FMT_YUVJ420P)) || guarded_render) {
if (!this->is_direct_rendering_disabled) {
@@ -322,8 +351,8 @@ static int get_buffer(AVCodecContext *context, AVFrame *av_frame){
if((width != this->bih.biWidth) || (height != this->bih.biHeight)) {
if(this->stream->video_out->get_capabilities(this->stream->video_out) & VO_CAP_CROP) {
- this->crop_right = width - this->bih.biWidth;
- this->crop_bottom = height - this->bih.biHeight;
+ crop_right = width - this->bih.biWidth - this->edge;
+ crop_bottom = height - this->bih.biHeight - this->edge;
} else {
if (!this->is_direct_rendering_disabled) {
xprintf(this->stream->xine, XINE_VERBOSITY_LOG,
@@ -349,6 +378,8 @@ static int get_buffer(AVCodecContext *context, AVFrame *av_frame){
av_frame->opaque = img;
+ av_frame->extended_data = av_frame->data;
+
av_frame->data[0]= img->base[0];
av_frame->data[1]= img->base[1];
av_frame->data[2]= img->base[2];
@@ -357,6 +388,16 @@ static int get_buffer(AVCodecContext *context, AVFrame *av_frame){
av_frame->linesize[1] = img->pitches[1];
av_frame->linesize[2] = img->pitches[2];
+ if (this->output_format == XINE_IMGFMT_YV12) {
+ av_frame->data[0] += (img->pitches[0] + 1) * this->edge;
+ av_frame->data[1] += (img->pitches[1] + 1) * this->edge / 2;
+ av_frame->data[2] += (img->pitches[2] + 1) * this->edge / 2;
+ img->crop_left = this->edge;
+ img->crop_top = this->edge;
+ img->crop_right = crop_right;
+ img->crop_bottom = crop_bottom;
+ }
+
/* We should really keep track of the ages of xine frames (see
* avcodec_default_get_buffer in libavcodec/utils.c)
* For the moment tell ffmpeg that every frame is new (age = bignumber) */
@@ -436,9 +477,14 @@ static const int skip_loop_filter_enum_values[] = {
};
#ifdef ENABLE_VAAPI
+/* TJ. libavcodec calls this with a list of supported pixel formats and lets us choose 1.
+ Returning PIX_FMT_VAAPI_VLD enables VAAPI.
+ However, at this point we only got image width and height from container, being unreliable
+ or zero (MPEG-TS). Thus we repeat vaapi_context initialization in get_buffer when needed.
+ This should be OK since NAL unit parsing is always done in software. */
static enum PixelFormat get_format(struct AVCodecContext *context, const enum PixelFormat *fmt)
{
- int i, profile;
+ int i;
ff_video_decoder_t *this = (ff_video_decoder_t *)context->opaque;
if(!this->class->enable_vaapi || !this->accel_img)
@@ -450,12 +496,21 @@ static enum PixelFormat get_format(struct AVCodecContext *context, const enum Pi
if (fmt[i] != PIX_FMT_VAAPI_VLD)
continue;
- profile = accel->profile_from_imgfmt(this->accel_img, fmt[i], context->codec_id, this->class->vaapi_mpeg_softdec);
+ this->vaapi_profile = accel->profile_from_imgfmt (this->accel_img, fmt[i],
+ context->codec_id, this->class->vaapi_mpeg_softdec);
- if (profile >= 0) {
+ if (this->vaapi_profile >= 0) {
+ int width = context->width;
+ int height = context->height;
VAStatus status;
- status = accel->vaapi_init(this->accel_img, profile, context->width, context->height, 0);
+ if (!width || !height) {
+ width = 1920;
+ height = 1080;
+ }
+ this->vaapi_width = width;
+ this->vaapi_height = height;
+ status = accel->vaapi_init (this->accel_img, this->vaapi_profile, width, height, 0);
if( status == VA_STATUS_SUCCESS ) {
ff_vaapi_context_t *va_context = accel->get_context(this->accel_img);
@@ -513,10 +568,20 @@ static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type)
_x_stream_info_get(this->stream, XINE_STREAM_INFO_VIDEO_FOURCC);
- /* Some codecs (eg rv10) copy flags in init so it's necessary to set
- * this flag here in case we are going to use direct rendering */
+ this->stream->video_out->open (this->stream->video_out, this->stream);
+
+ this->edge = 0;
if(this->codec->capabilities & CODEC_CAP_DR1 && this->class->enable_dri) {
- this->context->flags |= CODEC_FLAG_EMU_EDGE;
+ if (this->stream->video_out->get_capabilities (this->stream->video_out) & VO_CAP_CROP) {
+ /* We can crop. Fine. Lets allow decoders to paint over the frame edges.
+ This will be slightly faster. And it is also a workaround for buggy
+ v54 who likes to ignore EMU_EDGE for wmv2 and xvid. */
+ this->edge = avcodec_get_edge_width ();
+ } else {
+ /* Some codecs (eg rv10) copy flags in init so it's necessary to set
+ * this flag here in case we are going to use direct rendering */
+ this->context->flags |= CODEC_FLAG_EMU_EDGE;
+ }
}
/* TJ. without this, it wont work at all on my machine */
@@ -575,6 +640,7 @@ static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type)
free(this->context);
this->context = NULL;
_x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HANDLED, 0);
+ this->stream->video_out->close (this->stream->video_out, this->stream);
return;
}
@@ -590,6 +656,7 @@ static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type)
free(this->context);
this->context = NULL;
_x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HANDLED, 0);
+ this->stream->video_out->close (this->stream->video_out, this->stream);
return;
}
}
@@ -620,8 +687,6 @@ static void init_video_codec (ff_video_decoder_t *this, unsigned int codec_type)
set_stream_info(this);
}
- (this->stream->video_out->open) (this->stream->video_out, this->stream);
-
this->skipframes = 0;
/* flag for interlaced streams */
@@ -1450,9 +1515,6 @@ static void ff_handle_mpeg12_buffer (ff_video_decoder_t *this, buf_element_t *bu
else
img->duration = this->video_step;
- img->crop_right = this->crop_right;
- img->crop_bottom = this->crop_bottom;
-
#ifdef ENABLE_VAAPI
if( this->context->pix_fmt == PIX_FMT_VAAPI_VLD) {
if(this->accel->guarded_render(this->accel_img)) {
@@ -1731,6 +1793,8 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) {
this->aspect_ratio,
this->output_format,
VO_BOTH_FIELDS|this->frame_flags);
+ img->crop_right = img->width - this->bih.biWidth;
+ img->crop_bottom = img->height - this->bih.biHeight;
free_img = 1;
} else {
/* DR1 */
@@ -1745,19 +1809,22 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) {
if(this->pp_available && this->pp_quality && this->context->pix_fmt != PIX_FMT_VAAPI_VLD) {
if(this->av_frame->opaque) {
- /* DR1 */
+ /* DR1: filter into a new frame. Same size to avoid reallcation, just move the
+ image to top left corner. */
img = this->stream->video_out->get_frame (this->stream->video_out,
- (img->width + 15) & ~15,
- (img->height + 15) & ~15,
+ img->width,
+ img->height,
this->aspect_ratio,
this->output_format,
VO_BOTH_FIELDS|this->frame_flags);
+ img->crop_right = img->width - this->bih.biWidth;
+ img->crop_bottom = img->height - this->bih.biHeight;
free_img = 1;
}
pp_postprocess((const uint8_t **)this->av_frame->data, this->av_frame->linesize,
img->base, img->pitches,
- img->width, img->height,
+ this->bih.biWidth, this->bih.biHeight,
this->av_frame->qscale_table, this->av_frame->qstride,
this->our_mode, this->our_context,
this->av_frame->pict_type);
@@ -1786,10 +1853,6 @@ static void ff_handle_buffer (ff_video_decoder_t *this, buf_element_t *buf) {
else
img->duration = video_step_to_use;
- /* additionally crop away the extra pixels due to adjusting frame size above */
- img->crop_right = img->width - this->bih.biWidth;
- img->crop_bottom = img->height - this->bih.biHeight;
-
/* transfer some more frame settings for deinterlacing */
img->progressive_frame = !this->av_frame->interlaced_frame;
img->top_field_first = this->av_frame->top_field_first;
diff --git a/src/combined/ffmpeg/ffmpeg_compat.h b/src/combined/ffmpeg/ffmpeg_compat.h
index 5a44ae2c8..588cce247 100644
--- a/src/combined/ffmpeg/ffmpeg_compat.h
+++ b/src/combined/ffmpeg/ffmpeg_compat.h
@@ -146,4 +146,8 @@
# define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000
#endif
+#if LIBAVCODEC_VERSION_INT < ((52<<16)|(66<<8))
+# define avcodec_get_edge_width() (16)
+#endif
+
#endif /* XINE_AVCODEC_COMPAT_H */
diff --git a/src/combined/ffmpeg/xine_video.list b/src/combined/ffmpeg/xine_video.list
index 009f4dafd..6364c452e 100644
--- a/src/combined/ffmpeg/xine_video.list
+++ b/src/combined/ffmpeg/xine_video.list
@@ -70,7 +70,7 @@ WNV1 WNV1 Winnow Video
XL VIXL Miro/Pinnacle VideoXL
RT21 INDEO2 Indeo/RealTime 2
FPS1 FRAPS Fraps
-MPEG MPEG1VIDEO MPEG 1/2
+MPEG MPEG2VIDEO MPEG 1/2
CSCD CSCD CamStudio
AVS AVS AVS
ALGMM MMVIDEO American Laser Games MM
diff --git a/src/demuxers/demux_flv.c b/src/demuxers/demux_flv.c
index 090fe1097..cf1974659 100644
--- a/src/demuxers/demux_flv.c
+++ b/src/demuxers/demux_flv.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004 the xine project
+ * Copyright (C) 2004-2013 the xine project
*
* This file is part of xine, a free video player.
*
@@ -22,9 +22,16 @@
* Flash Video (.flv) File Demuxer
* by Mike Melanson (melanson@pcisys.net) and
* Claudio Ciccani (klan@users.sf.net)
+ * rewritten by Torsten Jager (t.jager@gmx.de)
*
* For more information on the FLV file format, visit:
- * http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v9.pdf
+ * http://www.adobe.com/devnet/flv/pdf/video_file_format_spec_v10.pdf
+ *
+ * TJ. FLV actually is a persistent variant of Realtime Messaging Protocol
+ * (rtmp). Some features, most notably message interleaving and relative
+ * timestamps, have been removed. Official spec imposes further restrictions.
+ * We should nevertheless be prepared for more general stuff left by rtmp
+ * stream recorders.
*/
#ifdef HAVE_CONFIG_H
@@ -47,11 +54,10 @@
#include <xine/compat.h>
#include <xine/demux.h>
#include "bswap.h"
-#include "group_games.h"
typedef struct {
unsigned int pts;
- unsigned int offset;
+ off_t offset;
} flv_index_entry_t;
typedef struct {
@@ -68,8 +74,7 @@ typedef struct {
off_t start; /* in bytes */
off_t size; /* in bytes */
- unsigned char got_video_header;
- unsigned char got_audio_header;
+ unsigned char got_video_header, got_audio_header, got_info;
unsigned int length; /* in ms */
int width;
@@ -79,7 +84,7 @@ typedef struct {
int samplerate;
int samplesize;
- int stereo;
+ int audio_channels;
int audiocodec;
off_t filesize;
@@ -92,51 +97,63 @@ typedef struct {
int64_t last_pts[2];
int send_newpts;
int buf_flag_seek;
+
+ int audiodelay; /* fine tune a/v sync */
+
+ unsigned int zero_pts_count;
} demux_flv_t ;
typedef struct {
demux_class_t demux_class;
} demux_flv_class_t;
+/* an early FLV specification had 24bit bigendian timestamps. This
+ limited clip duration to 4:39:37.215. The backwards compatible solution:
+ hand over 1 byte from stream ID. Hence the weird byte order 2-1-0-3 */
+#define gettimestamp(p,o) (((((((uint32_t)p[o+3]<<8)|p[o])<<8)|p[o+1])<<8)|p[o+2])
#define FLV_FLAG_HAS_VIDEO 0x01
#define FLV_FLAG_HAS_AUDIO 0x04
#define FLV_TAG_TYPE_AUDIO 0x08
#define FLV_TAG_TYPE_VIDEO 0x09
-#define FLV_TAG_TYPE_SCRIPT 0x12
-
-#define FLV_SOUND_FORMAT_PCM_BE 0x00
-#define FLV_SOUND_FORMAT_ADPCM 0x01
-#define FLV_SOUND_FORMAT_MP3 0x02
-#define FLV_SOUND_FORMAT_PCM_LE 0x03
-#define FLV_SOUND_FORMAT_NELLY16 0x04 /* Nellymoser 16KHz */
-#define FLV_SOUND_FORMAT_NELLY8 0x05 /* Nellymoser 8KHz */
-#define FLV_SOUND_FORMAT_NELLY 0x06 /* Nellymoser */
-#define FLV_SOUND_FORMAT_ALAW 0x07 /* G.711 A-LAW */
-#define FLV_SOUND_FORMAT_MULAW 0x08 /* G.711 MU-LAW */
-#define FLV_SOUND_FORMAT_AAC 0x0a
-#define FLV_SOUND_FORMAT_MP38 0x0e /* MP3 8KHz */
-
-#define FLV_VIDEO_FORMAT_FLV1 0x02 /* Sorenson H.263 */
-#define FLV_VIDEO_FORMAT_SCREEN 0x03
-#define FLV_VIDEO_FORMAT_VP6 0x04 /* On2 VP6 */
-#define FLV_VIDEO_FORMAT_VP6A 0x05 /* On2 VP6 with alphachannel */
-#define FLV_VIDEO_FORMAT_SCREEN2 0x06
-#define FLV_VIDEO_FORMAT_H264 0x07
-
-#define FLV_DATA_TYPE_NUMBER 0x00
-#define FLV_DATA_TYPE_BOOL 0x01
-#define FLV_DATA_TYPE_STRING 0x02
-#define FLV_DATA_TYPE_OBJECT 0x03
-#define FLC_DATA_TYPE_CLIP 0x04
-#define FLV_DATA_TYPE_REFERENCE 0x07
-#define FLV_DATA_TYPE_ECMARRAY 0x08
-#define FLV_DATA_TYPE_ENDOBJECT 0x09
-#define FLV_DATA_TYPE_ARRAY 0x0a
-#define FLV_DATA_TYPE_DATE 0x0b
-#define FLV_DATA_TYPE_LONGSTRING 0x0c
-
+#define FLV_TAG_TYPE_NOTIFY 0x12
+
+typedef enum {
+ AF_PCM_BE, /* officially "native endian"?? */
+ AF_ADPCM,
+ AF_MP3,
+ AF_PCM_LE, /* little endian */
+ AF_NELLY16, /* Nellymoser 16KHz */
+ AF_NELLY8, /* Nellymoser 8KHz */
+ AF_NELLY, /* Nellymoser */
+ AF_ALAW, /* G.711 A-LAW */
+ AF_MULAW, /* G.711 MU-LAW */
+ AF_reserved9,
+ AF_AAC, /* mp4a with global header */
+ AF_SPEEX,
+ AF_reserved12,
+ AF_reserved13,
+ AF_MP38, /* MP3 8KHz */
+ AF_DS /* device specific sound */
+} af_t;
+
+/* audio types that support free samplerate from header */
+/* got the message ? ;-) */
+#define IS_PCM(id) ((((1<<AF_PCM_BE)|(1<<AF_ADPCM)|(1<<AF_PCM_LE)|(1<<AF_ALAW)|(1<<AF_MULAW))>>(id))&1)
+
+typedef enum {
+ VF_reserved0,
+ VF_JPEG,
+ VF_FLV1, /* modified Sorenson H.263 */
+ VF_SCREEN, /* Macromedia screen video v1 */
+ VF_VP6, /* On2 VP6 */
+ VF_VP6A, /* On2 VP6 with alphachannel */
+ VF_SCREEN2, /* v2 */
+ VF_H264, /* MPEG4 part 10, usually with global sequence parameter set */
+ VF_H263,
+ VF_MP4 /* MPEG4 part 2, usually with global sequence parameter set */
+} vf_t;
/* redefine abs as macro to handle 64-bit diffs.
i guess llabs may not be available everywhere */
@@ -146,27 +163,24 @@ typedef struct {
#define PTS_AUDIO 0
#define PTS_VIDEO 1
-static void check_newpts(demux_flv_t *this, int64_t pts, int video) {
+static void check_newpts (demux_flv_t *this, int64_t pts, int video) {
int64_t diff;
-
- diff = pts - this->last_pts[video];
lprintf ("check_newpts %"PRId64"\n", pts);
-
- if (pts && (this->send_newpts || (this->last_pts[video] && abs(diff)>WRAP_THRESHOLD))) {
- lprintf ("diff=%"PRId64"\n", diff);
-
- if (this->buf_flag_seek) {
- _x_demux_control_newpts(this->stream, pts, BUF_FLAG_SEEK);
- this->buf_flag_seek = 0;
- } else {
- _x_demux_control_newpts(this->stream, pts, 0);
+ if (this->buf_flag_seek) {
+ _x_demux_control_newpts (this->stream, pts, BUF_FLAG_SEEK);
+ this->buf_flag_seek = 0;
+ this->send_newpts = 0;
+ this->last_pts[1 - video] = 0;
+ } else {
+ diff = pts - this->last_pts[video];
+ if (pts && this->last_pts[video] && abs (diff) > WRAP_THRESHOLD) {
+ lprintf ("diff=%"PRId64"\n", diff);
+ _x_demux_control_newpts (this->stream, pts, 0);
+ this->send_newpts = 0;
+ this->last_pts[1-video] = 0;
}
- this->send_newpts = 0;
- this->last_pts[1-video] = 0;
}
-
- if (pts)
- this->last_pts[video] = pts;
+ this->last_pts[video] = pts;
}
/* returns 1 if the FLV file was opened successfully, 0 otherwise */
@@ -209,250 +223,349 @@ static int open_flv_file(demux_flv_t *this) {
_tmp.d;\
})\
-static int parse_flv_var(demux_flv_t *this,
- unsigned char *buf, int size, char *key, int keylen) {
- unsigned char *tmp = buf;
- unsigned char *end = buf + size;
- char *str;
- unsigned char type;
- unsigned int len, num;
-
- if (size < 1)
- return 0;
-
- type = *tmp++;
-
- switch (type) {
- case FLV_DATA_TYPE_NUMBER:
- lprintf(" got number (%f)\n", BE_F64(tmp));
- if (key) {
- double val = BE_F64(tmp);
- if (keylen == 8 && !strncmp(key, "duration", 8)) {
- this->length = val * 1000.0;
- }
- else if (keylen == 5 && !strncmp(key, "width", 5)) {
- this->width = val;
- _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_WIDTH, this->width);
- }
- else if (keylen == 6 && !strncmp(key, "height", 6)) {
- this->height = val;
- _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HEIGHT, this->height);
- }
- else if (keylen == 9 && !strncmp(key, "framerate", 9)) {
- if (val > 0) {
- this->duration = 90000.0 / val;
- _x_stream_info_set(this->stream, XINE_STREAM_INFO_FRAME_DURATION, this->duration);
+/* Action Message Format data types */
+typedef enum {
+ AMF0_NUMBER = 0x00, /* double_be */
+ AMF0_BOOLEAN = 0x01, /* 1 byte TRUE or FALSE */
+ AMF0_STRING = 0x02, /* u16_be length, then utf8 string without end byte */
+ AMF0_OBJECT = 0x03, /* name/type/data triplets, then empty name plus
+ AMF0_OBJECT_END. name stored same way as AMF0_STRING */
+ AMF0_MOVIECLIP = 0x04, /* reserved */
+ AMF0_NULL_VALUE = 0x05, /* no data */
+ AMF0_UNDEFINED = 0x06, /* no data */
+ AMF0_REFERENCE = 0x07, /* u16be index into previous items table */
+ AMF0_ECMA_ARRAY = 0x08, /* u32_be number_of_entries, then same as AMF0_OBJECT */
+ AMF0_OBJECT_END = 0x09, /* end marker of AMF0_OBJECT */
+ AMF0_STRICT_ARRAY = 0x0a, /* u32_be n, then exactly n type/value pairs */
+ AMF0_DATE = 0x0b, /* double_be milliseconds since Jan 01, 1970, then
+ s16_be minutes off UTC */
+ AMF0_LONG_STRING = 0x0c, /* u32_be length, then utf8 string */
+ AMF0_UNSUPPORTED = 0x0d, /* no data */
+ AMF0_RECORD_SET = 0x0e, /* reserved */
+ AMF0_XML_OBJECT = 0x0f, /* physically same as AMF0_LONG_STRING */
+ AMF0_TYPED_OBJECT = 0x10, /* very complex, should not appear in FLV */
+ AMF0_AMF3 = 0x11, /* switch to AMF3 from here */
+} amf_type_t;
+
+#define MAX_AMF_LEVELS 10
+#define SPC (space + 2 * (MAX_AMF_LEVELS - level))
+#define NEEDBYTES(n) if ((unsigned long int)(end - p) < n) return 0
+
+static int parse_amf (demux_flv_t *this, unsigned char *buf, int size) {
+ unsigned char *p = buf, *end = buf + size, *name, space[2 * MAX_AMF_LEVELS + 3];
+ int level = 0, i, type, count[MAX_AMF_LEVELS], info = 0;
+ unsigned int u, c;
+ time_t tsecs;
+ struct tm *tstruct;
+ double val;
+
+ /* init prettyprinter */
+ memset (space, ' ', 2 * MAX_AMF_LEVELS + 2);
+ space[2 * MAX_AMF_LEVELS + 2] = 0x00;
+ /* top level has nameless vars */
+ count[0] = 10000;
+ while (1) {
+ if (count[level] > 0) {
+ /* next strict array item */
+ if (--count[level] == 0) {
+ /* one level up */
+ if (--level < 0) return 0;
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG, "%s}\n", SPC);
+ continue;
+ }
+ if (p >= end) break;
+ type = *p++;
+ name = NULL;
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG, "%s", SPC);
+ } else {
+ /* get current name */
+ NEEDBYTES (2);
+ u = _X_BE_16 (p);
+ p += 2;
+ NEEDBYTES (u);
+ name = p;
+ p += u;
+ if (u == 0) {
+ /* object end, 1 level up */
+ if (--level < 0) return 0;
+ if ((p < end) && (*p == AMF0_OBJECT_END)) p++;
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG, "%s}\n", SPC);
+ continue;
+ }
+ NEEDBYTES (1);
+ type = *p;
+ *p++ = 0x00;
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG, "%s%s = ", SPC, name);
+ }
+ switch (type) {
+ case AMF0_NUMBER:
+ NEEDBYTES (8);
+ val = BE_F64 (p);
+ i = val;
+ if (i == val) xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "%d\n", i);
+ else xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "%.03lf\n", val);
+ p += 8;
+ if (name && info) {
+ if (!strcmp (name, "duration")) {
+ this->length = val * (double)1000;
+ } else if (!strcmp (name, "width")) {
+ this->width = i;
+ _x_stream_info_set (this->stream, XINE_STREAM_INFO_VIDEO_WIDTH, i);
+ } else if (!strcmp (name, "height")) {
+ this->height = i;
+ _x_stream_info_set (this->stream, XINE_STREAM_INFO_VIDEO_HEIGHT, i);
+ } else if (!strcmp (name, "framerate") || !strcmp (name, "videoframerate")) {
+ if ((i > 0) && (i < 1000)) {
+ this->duration = (double)90000 / val;
+ _x_stream_info_set (this->stream, XINE_STREAM_INFO_FRAME_DURATION,
+ this->duration);
+ }
+ } else if (!strcmp (name, "videodatarate")) {
+ _x_stream_info_set (this->stream, XINE_STREAM_INFO_VIDEO_BITRATE,
+ val * (double)1000);
+ } else if (!strcmp (name, "videocodecid")) {
+ this->videocodec = i;
+ } else if (!strcmp (name, "audiosamplerate")) {
+ this->samplerate = i;
+ _x_stream_info_set (this->stream, XINE_STREAM_INFO_AUDIO_SAMPLERATE, i);
+ } else if (!strcmp (name, "audiosamplesize")) {
+ this->samplesize = i;
+ _x_stream_info_set (this->stream, XINE_STREAM_INFO_AUDIO_BITS, i);
+ } else if (!strcmp (name, "stereo")) {
+ this->audio_channels = i ? 2 : 1;
+ _x_stream_info_set (this->stream, XINE_STREAM_INFO_AUDIO_CHANNELS, i);
+ } else if (!strcmp (name, "audiodatarate")) {
+ _x_stream_info_set (this->stream, XINE_STREAM_INFO_AUDIO_BITRATE,
+ val * (double)1000);
+ } else if (!strcmp (name, "audiocodecid")) {
+ this->audiocodec = i;
+ } else if (!strcmp (name, "filesize")) {
+ this->filesize = val;
+ } else if (!strcmp (name, "audiodelay")) {
+ this->audiodelay = val * (double)-1000;
}
}
- else if (keylen == 13 && !strncmp(key, "videodatarate", 13)) {
- _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_BITRATE, val*1000.0);
- }
- else if (keylen == 12 && !strncmp(key, "videocodecid", 12)) {
- this->videocodec = val;
- }
- else if (keylen == 15 && !strncmp(key, "audiosamplerate", 15)) {
- this->samplerate = val;
- _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_SAMPLERATE, this->samplerate);
- }
- else if (keylen == 15 && !strncmp(key, "audiosamplesize", 15)) {
- this->samplesize = val;
- _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_BITS, this->samplesize);
- }
- else if (keylen == 5 && !strncmp(key, "stereo", 5)) {
- this->stereo = val;
- _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_CHANNELS, this->stereo ? 2 : 1);
- }
- else if (keylen == 13 && !strncmp(key, "audiodatarate", 13)) {
- _x_stream_info_set(this->stream, XINE_STREAM_INFO_AUDIO_BITRATE, val*1000.0);
- }
- else if (keylen == 12 && !strncmp(key, "audiocodecid", 12)) {
- this->audiocodec = val;
- }
- else if (keylen == 8 && !strncmp(key, "filesize", 8)) {
- this->filesize = val;
- }
- }
- tmp += 8;
break;
- case FLV_DATA_TYPE_BOOL:
- lprintf(" got bool (%d)\n", *tmp);
- tmp++;
- break;
- case FLV_DATA_TYPE_STRING:
- lprintf(" got string (%s)\n", tmp+2);
- len = _X_BE_16(tmp);
- tmp += len + 2;
+ case AMF0_BOOLEAN:
+ NEEDBYTES (1);
+ i = !!(*p++);
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "%s\n", i ? "yes" : "no");
+ if (name && info) {
+ if (!strcmp (name, "stereo"))
+ this->audio_channels = i ? 2 : 1;
+ }
break;
- case FLV_DATA_TYPE_OBJECT:
- while ((len = _X_BE_16(tmp)) && tmp < end) {
- lprintf(" got object var (%s)\n", tmp+2);
- str = tmp + 2;
- tmp += len + 2;
- len = parse_flv_var(this, tmp, end-tmp, str, len);
- if (!len)
- return 0;
- tmp += len;
- }
- if (*tmp++ != FLV_DATA_TYPE_ENDOBJECT)
- return 0;
+ case AMF0_STRING:
+ NEEDBYTES (2);
+ u = _X_BE_16 (p);
+ p += 2;
+ NEEDBYTES (u);
+ c = p[u];
+ p[u] = 0x00;
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "\"%s\"\n", p);
+ if (!level && !strcmp (p, "onMetaData")) info = this->got_info = 1;
+ if (name && info) {
+ if ((!strcmp (name, "audiocodecid")) && (!strcmp (p, "mp4a")))
+ this->audiocodec = AF_AAC;
+ else if ((!strcmp (name, "videocodecid")) && (!strcmp (p, "avc1")))
+ this->videocodec = VF_H264;
+ else if (!strcmp (name, "stereo")) {
+ if (!strcmp (p, "true") || !strcmp (p, "yes"))
+ this->audio_channels = 2;
+ }
+ }
+ p[u] = c;
+ p += u;
break;
- case FLV_DATA_TYPE_ECMARRAY:
- lprintf(" got EMCA array (%d indices)\n", _X_BE_32(tmp));
- num = _X_BE_32(tmp);
- tmp += 4;
- while (num-- && tmp < end) {
- lprintf(" got array key (%s)\n", tmp+2);
- len = _X_BE_16(tmp);
- str = tmp + 2;
- tmp += len + 2;
- len = parse_flv_var(this, tmp, end-tmp, str, len);
- if (!len)
- return 0;
- tmp += len;
- }
+ case AMF0_LONG_STRING:
+ case AMF0_XML_OBJECT:
+ NEEDBYTES (4);
+ u = _X_BE_32 (p);
+ p += 4;
+ NEEDBYTES (u);
+ /* avoid printf() overload */
+ if (u > 4000) p[4000] = 0x00;
+ c = p[u];
+ p[u] = 0x00;
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "%s\n", p);
+ p[u] = c;
+ p += u;
break;
- case FLV_DATA_TYPE_ARRAY:
- lprintf(" got array (%d indices)\n", _X_BE_32(tmp));
- num = _X_BE_32(tmp);
- tmp += 4;
- if (key && keylen == 5 && !strncmp(key, "times", 5)) {
- if (!this->index || this->num_indices != num) {
- if (this->index)
- free(this->index);
- this->index = calloc(num, sizeof(flv_index_entry_t));
- if (!this->index)
- return 0;
- this->num_indices = num;
+ case AMF0_ECMA_ARRAY:
+ NEEDBYTES (4);
+ u = _X_BE_32 (p); /* this value is unreliable */
+ p += 4;
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "[%d] ", u);
+ /* fall through */
+ case AMF0_OBJECT:
+ if (++level >= MAX_AMF_LEVELS) return 0;
+ count[level] = -1;
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "{\n");
+ continue;
+ case AMF0_STRICT_ARRAY:
+ NEEDBYTES (4);
+ u = _X_BE_32 (p);
+ p += 4;
+ c = 0;
+ if (name) {
+ if (!strcmp (name, "times")) c = 1;
+ else if (!strcmp (name, "filepositions")) c = 2;
}
- for (num = 0; num < this->num_indices && tmp < end; num++) {
- if (*tmp++ == FLV_DATA_TYPE_NUMBER) {
- lprintf(" got number (%f)\n", BE_F64(tmp));
- this->index[num].pts = BE_F64(tmp) * 1000.0;
- tmp += 8;
+ if (c) {
+ NEEDBYTES (u * 9);
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "[%d] {..}\n", u);
+ if (!this->index || (this->num_indices != u)) {
+ if (this->index) free (this->index);
+ this->index = calloc (u, sizeof (flv_index_entry_t));
+ if (!this->index) return 0;
+ this->num_indices = u;
}
- }
- break;
- }
- if (key && keylen == 13 && !strncmp(key, "filepositions", 13)) {
- if (!this->index || this->num_indices != num) {
- if (this->index)
- free(this->index);
- this->index = calloc(num, sizeof(flv_index_entry_t));
- if (!this->index)
- return 0;
- this->num_indices = num;
- }
- for (num = 0; num < this->num_indices && tmp < end; num++) {
- if (*tmp++ == FLV_DATA_TYPE_NUMBER) {
- lprintf(" got number (%f)\n", BE_F64(tmp));
- this->index[num].offset = BE_F64(tmp);
- tmp += 8;
+ if (c == 1) for (i = 0; i < (int)u; i++) {
+ if (*p++ != AMF0_NUMBER) return 0;
+ this->index[i].pts = BE_F64 (p) * (double)1000;
+ p += 8;
+ } else for (i = 0; i < (int)u; i++) {
+ if (*p++ != AMF0_NUMBER) return 0;
+ this->index[i].offset = BE_F64 (p);
+ p += 8;
}
+ } else {
+ if (++level >= MAX_AMF_LEVELS) return 0;
+ count[level] = u + 1;
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "[%d] {\n", u);
}
- break;
- }
- while (num-- && tmp < end) {
- len = parse_flv_var(this, tmp, end-tmp, NULL, 0);
- if (!len)
- return 0;
- tmp += len;
- }
break;
- case FLV_DATA_TYPE_DATE:
- lprintf(" got date (%"PRId64", %d)\n", _X_BE_64(tmp), _X_BE_16(tmp+8));
- tmp += 10;
+ case AMF0_DATE:
+ NEEDBYTES (10);
+ val = BE_F64 (p) / (double)1000;
+ tsecs = val;
+ p += 8;
+ i = _X_BE_16 (p);
+ p += 2;
+ if (i & 0x8000) i |= ~0x7fff;
+ tsecs += i * 60;
+ tstruct = gmtime (&tsecs);
+ if (tstruct) {
+ char ts[200];
+ if (strftime (ts, 200, "%x %X", tstruct) >= 0)
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "%s\n", ts);
+ }
break;
- default:
- lprintf(" got type %d\n", type);
+ case AMF0_NULL_VALUE:
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "(null)\n");
break;
- }
-
- return (tmp - buf);
-}
-
-static void parse_flv_script(demux_flv_t *this, int size) {
- unsigned char *buf = malloc(size);
- unsigned char *tmp = buf;
- unsigned char *end = buf + size;
- int len;
-
- if (!buf || this->input->read(this->input, buf, size ) != size) {
- this->status = DEMUX_FINISHED;
- free(buf);
- return;
- }
-
- while (tmp < end) {
- len = parse_flv_var(this, tmp, end-tmp, NULL, 0);
- if (len < 1)
+ case AMF0_UNDEFINED:
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "(undefined)\n");
+ break;
+ case AMF0_REFERENCE:
+ NEEDBYTES (2);
+ u = _X_BE_16 (p);
+ p += 2;
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "(see #%u)\n", u);
+ break;
+ default:
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "(unhandled type %d)\n", type);
+ return 0;
break;
- tmp += len;
+ }
}
-
- free(buf);
+ return level == 0;
}
+#define GETBYTES(n) \
+ if (remaining_bytes < n) \
+ continue; \
+ if (this->input->read (this->input, (char *)buffer + 16, n) != n) \
+ goto fail; \
+ remaining_bytes -= n;
+
static int read_flv_packet(demux_flv_t *this, int preview) {
fifo_buffer_t *fifo = NULL;
buf_element_t *buf = NULL;
+ unsigned int tag_type, mp4header, buf_type = 0;
+ unsigned int buf_flags = BUF_FLAG_FRAME_START;
+ int remaining_bytes = 0;
+
+ unsigned int pts; /* ms */
+ int ptsoffs = 0; /* pts ticks */
+ int64_t buf_pts;
+ off_t size, normpos = 0;
+
+ this->status = DEMUX_OK;
while (1) {
- unsigned char buffer[12], extrabuffer[4];
- unsigned char tag_type, avinfo;
- unsigned int remaining_bytes;
- unsigned int buf_type = 0;
- unsigned int buf_flags = 0;
- unsigned int pts;
-
- lprintf (" reading FLV tag...\n");
- this->input->seek(this->input, 4, SEEK_CUR);
- if (this->input->read(this->input, buffer, 11) != 11) {
+ /* define this here to prevent compiler caching it across multiple this->input->read ()'s.
+ layout:
+ 0 .. 3 previous tags footer (not needed here)
+ 4 tag type
+ 5 .. 7 payload size
+ 8 .. 11 dts (= pts unless H.264 or MPEG4)
+ 12 .. 14 stream id (usually 0)
+ 15 codec id / frame type / audio params (A/V only)
+ 16 right / bottom crop (VP6(F))
+ frame subtype (AAC, H264, MPEG4)
+ 17 .. 19 ?? (VP6F)
+ pts - dts (H264, MPEG4) */
+ unsigned char buffer[20];
+ /* skip rest, if any */
+ if (remaining_bytes)
+ this->input->seek (this->input, remaining_bytes, SEEK_CUR);
+ /* we have a/v tags mostly, optimize for them */
+ if (this->input->read (this->input, (char *)buffer, 16) != 16) {
+ fail:
this->status = DEMUX_FINISHED;
return this->status;
}
-
- tag_type = buffer[0];
- remaining_bytes = _X_BE_24(&buffer[1]);
- pts = _X_BE_24(&buffer[4]) | (buffer[7] << 24);
-
- lprintf(" tag_type = 0x%02X, 0x%X bytes, pts %u\n",
- tag_type, remaining_bytes, pts/90);
+ remaining_bytes = _X_BE_24(&buffer[5]);
+ /* skip empty tags */
+ if (--remaining_bytes < 0)
+ continue;
+ tag_type = buffer[4];
+ pts = gettimestamp (buffer, 8);
+ mp4header = 0;
switch (tag_type) {
case FLV_TAG_TYPE_AUDIO:
- lprintf(" got audio tag..\n");
- if (this->input->read(this->input, &avinfo, 1) != 1) {
- this->status = DEMUX_FINISHED;
- return this->status;
- }
- remaining_bytes--;
+ if (!pts)
+ this->zero_pts_count++;
+ else if (this->audiodelay > 0)
+ pts += this->audiodelay;
+ this->audiocodec = buffer[15] >> 4;
- this->audiocodec = avinfo >> 4; /* override */
switch (this->audiocodec) {
- case FLV_SOUND_FORMAT_PCM_BE:
+ case AF_PCM_BE:
buf_type = BUF_AUDIO_LPCM_BE;
- break;
- case FLV_SOUND_FORMAT_ADPCM:
+ break;
+ case AF_ADPCM:
buf_type = BUF_AUDIO_FLVADPCM;
- break;
- case FLV_SOUND_FORMAT_MP3:
- case FLV_SOUND_FORMAT_MP38:
+ break;
+ case AF_MP3:
+ case AF_MP38:
buf_type = BUF_AUDIO_MPEG;
- break;
- case FLV_SOUND_FORMAT_PCM_LE:
+ break;
+ case AF_PCM_LE:
buf_type = BUF_AUDIO_LPCM_LE;
- break;
- case FLV_SOUND_FORMAT_ALAW:
+ break;
+ case AF_ALAW:
buf_type = BUF_AUDIO_ALAW;
- break;
- case FLV_SOUND_FORMAT_MULAW:
+ break;
+ case AF_MULAW:
buf_type = BUF_AUDIO_MULAW;
- break;
- case FLV_SOUND_FORMAT_AAC:
+ break;
+#ifdef BUF_AUDIO_NELLYMOSER
+ case AF_NELLY:
+ case AF_NELLY8:
+ case AF_NELLY16:
+ buf_type = BUF_AUDIO_NELLYMOSER;
+ break;
+#endif
+ case AF_AAC:
buf_type = BUF_AUDIO_AAC;
- /* AAC extra header */
- this->input->read(this->input, extrabuffer, 1 );
- remaining_bytes--;
+ GETBYTES (1);
+ if (!buffer[16]) mp4header = 1;
+ break;
+ case AF_SPEEX:
+ buf_type = BUF_AUDIO_SPEEX;
break;
default:
lprintf(" unsupported audio format (%d)...\n", this->audiocodec);
@@ -461,272 +574,205 @@ static int read_flv_packet(demux_flv_t *this, int preview) {
}
fifo = this->audio_fifo;
- if (preview && !this->got_audio_header) {
+ if (!this->got_audio_header) {
+ /* prefer tag header settings, unless we hit some unofficial libavformat extension */
+ if (!IS_PCM (this->audiocodec) || (buffer[15] & 0x0c) || (this->samplerate < 4000)) {
+ this->samplerate =
+ this->audiocodec == AF_NELLY8 || this->audiocodec == AF_MP38 ? 8000 :
+ this->audiocodec == AF_NELLY16 ? 16000 :
+ 44100 >> (3 - ((buffer[15] >> 2) & 3));
+ this->audio_channels = (buffer[15] & 1) + 1;
+ }
+ if (!this->audio_channels)
+ this->audio_channels = (buffer[15] & 1) + 1;
/* send init info to audio decoder */
buf = fifo->buffer_pool_alloc(fifo);
buf->decoder_flags = BUF_FLAG_HEADER | BUF_FLAG_STDHEADER | BUF_FLAG_FRAME_END;
buf->decoder_info[0] = 0;
- buf->decoder_info[1] = 44100 >> (3 - ((avinfo >> 2) & 3)); /* samplerate */
- buf->decoder_info[2] = (avinfo & 2) ? 16 : 8; /* bits per sample */
- buf->decoder_info[3] = (avinfo & 1) + 1; /* channels */
+ buf->decoder_info[1] = this->samplerate;
+ buf->decoder_info[2] = (buffer[15] & 2) ? 16 : 8; /* bits per sample */
+ buf->decoder_info[3] = this->audio_channels;
buf->size = 0; /* no extra data */
buf->type = buf_type;
fifo->put(fifo, buf);
this->got_audio_header = 1;
- if (!INPUT_IS_SEEKABLE(this->input)) {
- /* stop preview processing immediately, this enables libfaad to
- * initialize even without INPUT_CAP_SEEKABLE of input stream.
- */
- preview = 0;
- }
}
- break;
+ break;
case FLV_TAG_TYPE_VIDEO:
- lprintf(" got video tag..\n");
- if (this->input->read(this->input, &avinfo, 1) != 1) {
- this->status = DEMUX_FINISHED;
- return this->status;
- }
- remaining_bytes--;
-
- switch ((avinfo >> 4)) {
- case 0x01:
- buf_flags = BUF_FLAG_KEYFRAME;
- break;
- case 0x05:
- /* skip server command */
- this->input->seek(this->input, remaining_bytes, SEEK_CUR);
- continue;
- default:
- break;
+ if (!pts)
+ this->zero_pts_count++;
+ else if (this->audiodelay < 0)
+ pts -= this->audiodelay;
+ /* check frame type */
+ switch (buffer[15] >> 4) {
+ case 1: /* Key or seekable frame */
+ case 4: /* server generated keyframe */
+ buf_flags |= BUF_FLAG_KEYFRAME;
+ break;
+ case 5:
+ /* This is a rtmp server command.
+ One known use:
+ When doing a rtmp time seek between key frames, server may send:
+ 1. a type 5 frame of one 0x00 byte
+ 2. the nearest keyframe before the seek time
+ 3. the following frames before the seek time, if any
+ 4. a type 5 frame of one 0x01 byte */
+ continue;
+ default: ;
}
-
- this->videocodec = avinfo & 0x0F; /* override */
+ this->videocodec = buffer[15] & 0x0F;
switch (this->videocodec) {
- case FLV_VIDEO_FORMAT_FLV1:
+ case VF_FLV1:
buf_type = BUF_VIDEO_FLV1;
- break;
- case FLV_VIDEO_FORMAT_VP6:
- buf_type = BUF_VIDEO_VP6F;
- /* VP6 extra header */
- this->input->read(this->input, extrabuffer, 1 );
- remaining_bytes--;
- break;
- case FLV_VIDEO_FORMAT_VP6A:
- buf_type = BUF_VIDEO_VP6F;
- /* VP6A extra header */
- this->input->read(this->input, extrabuffer, 4);
- remaining_bytes -= 4;
- break;
- case FLV_VIDEO_FORMAT_H264:
+ break;
+ case VF_H263:
+ buf_type = BUF_VIDEO_H263;
+ break;
+ case VF_MP4:
+ buf_type = BUF_VIDEO_MPEG4;
+ goto comm_mpeg4;
+ case VF_H264:
buf_type = BUF_VIDEO_H264;
/* AVC extra header */
- this->input->read(this->input, extrabuffer, 4);
- remaining_bytes -= 4;
- break;
+ comm_mpeg4:
+ GETBYTES (4);
+ if (buffer[16] == 2)
+ continue; /* skip sequence footer */
+ if (!buffer[16])
+ mp4header = 1;
+ /* pts really is dts here, buffer[17..19] has (pts - dts) signed big endian. */
+ ptsoffs = _X_BE_24 (buffer + 17);
+ if (ptsoffs & 0x800000)
+ ptsoffs |= ~0xffffff;
+ /* better: +/- 16 frames, but we cannot trust header framerate */
+ if ((ptsoffs < -1000) || (ptsoffs > 1000))
+ ptsoffs = 0;
+ ptsoffs *= 90;
+ break;
+ case VF_VP6:
+ buf_type = BUF_VIDEO_VP6F;
+ GETBYTES (1);
+ break;
+ case VF_VP6A:
+ buf_type = BUF_VIDEO_VP6F;
+ GETBYTES (4);
+ break;
+ case VF_JPEG:
+ buf_type = BUF_VIDEO_JPEG;
+ break;
default:
lprintf(" unsupported video format (%d)...\n", this->videocodec);
buf_type = BUF_VIDEO_UNKNOWN;
- break;
+ break;
}
fifo = this->video_fifo;
- if (preview && !this->got_video_header) {
+ if (!this->got_video_header) {
xine_bmiheader *bih;
/* send init info to video decoder; send the bitmapinfo header to the decoder
* primarily as a formality since there is no real data inside */
buf = fifo->buffer_pool_alloc(fifo);
- buf->decoder_flags = BUF_FLAG_HEADER | BUF_FLAG_STDHEADER |
- BUF_FLAG_FRAMERATE | BUF_FLAG_FRAME_END;
- buf->decoder_info[0] = this->duration;
+ buf->decoder_flags = BUF_FLAG_HEADER | BUF_FLAG_STDHEADER | BUF_FLAG_FRAME_END;
+ if (this->duration) {
+ buf->decoder_flags |= BUF_FLAG_FRAMERATE;
+ buf->decoder_info[0] = this->duration;
+ }
bih = (xine_bmiheader *) buf->content;
- memset(bih, 0, sizeof(xine_bmiheader));
- bih->biSize = sizeof(xine_bmiheader);
- bih->biWidth = this->width;
+ memset (bih, 0, sizeof(xine_bmiheader));
+ bih->biSize = sizeof(xine_bmiheader);
+ bih->biWidth = this->width;
bih->biHeight = this->height;
- buf->size = sizeof(xine_bmiheader);
- buf->type = buf_type;
+ buf->size = sizeof(xine_bmiheader);
+ buf->type = buf_type;
if (buf_type == BUF_VIDEO_VP6F) {
- *((unsigned char *)buf->content+buf->size) = extrabuffer[0];
+ *((unsigned char *)buf->content+buf->size) = buffer[16];
bih->biSize++;
buf->size++;
}
- else if (buf_type == BUF_VIDEO_H264 && extrabuffer[0] == 0) {
- /* AVC sequence header */
- if (remaining_bytes > buf->max_size-buf->size) {
- xprintf(this->xine, XINE_VERBOSITY_LOG,
- _("sequence header too big (%u bytes)!\n"), remaining_bytes);
- this->input->read(this->input, buf->content+buf->size, buf->max_size-buf->size);
- this->input->seek(this->input, remaining_bytes-buf->max_size-buf->size, SEEK_CUR);
- bih->biSize = buf->max_size;
- buf->size = buf->max_size;
- }
- else {
- this->input->read(this->input, buf->content+buf->size, remaining_bytes);
- bih->biSize += remaining_bytes;
- buf->size += remaining_bytes;
- }
- remaining_bytes = 0;
- }
fifo->put(fifo, buf);
this->got_video_header = 1;
}
- break;
-
- case FLV_TAG_TYPE_SCRIPT:
- lprintf(" got script tag...\n");
- if (preview) {
- parse_flv_script(this, remaining_bytes);
-
- /* send init info to decoders using script information as reference */
- if (!this->got_audio_header && this->audiocodec) {
- buf = this->audio_fifo->buffer_pool_alloc(this->audio_fifo);
- buf->decoder_flags = BUF_FLAG_HEADER | BUF_FLAG_STDHEADER | BUF_FLAG_FRAME_END;
- buf->decoder_info[0] = 0;
- buf->decoder_info[1] = this->samplerate;
- buf->decoder_info[2] = this->samplesize;
- buf->decoder_info[3] = this->stereo ? 2 : 1;
- switch (this->audiocodec) {
- case FLV_SOUND_FORMAT_PCM_BE:
- buf->type = BUF_AUDIO_LPCM_BE;
- break;
- case FLV_SOUND_FORMAT_ADPCM:
- buf->type = BUF_AUDIO_FLVADPCM;
- break;
- case FLV_SOUND_FORMAT_MP3:
- case FLV_SOUND_FORMAT_MP38:
- buf->type = BUF_AUDIO_MPEG;
- break;
- case FLV_SOUND_FORMAT_PCM_LE:
- buf->type = BUF_AUDIO_LPCM_LE;
- break;
- case FLV_SOUND_FORMAT_ALAW:
- buf->type = BUF_AUDIO_ALAW;
- break;
- case FLV_SOUND_FORMAT_MULAW:
- buf->type = BUF_AUDIO_MULAW;
- break;
- case FLV_SOUND_FORMAT_AAC:
- buf->type = BUF_AUDIO_AAC;
- break;
- default:
- buf->type = BUF_AUDIO_UNKNOWN;
- break;
- }
- buf->size = 0;
- this->audio_fifo->put(this->audio_fifo, buf);
- this->got_audio_header = 1;
- lprintf(" got audio header from metadata...\n");
- }
+ break;
- if (!this->got_video_header && this->videocodec && this->videocodec != FLV_VIDEO_FORMAT_H264) {
- xine_bmiheader *bih;
- buf = this->video_fifo->buffer_pool_alloc(this->video_fifo);
- buf->decoder_flags = BUF_FLAG_HEADER | BUF_FLAG_STDHEADER |
- BUF_FLAG_FRAMERATE | BUF_FLAG_FRAME_END;
- buf->decoder_info[0] = this->duration;
- switch (this->videocodec) {
- case FLV_VIDEO_FORMAT_FLV1:
- buf->type = BUF_VIDEO_FLV1;
- break;
- case FLV_VIDEO_FORMAT_VP6:
- case FLV_VIDEO_FORMAT_VP6A:
- buf->type = BUF_VIDEO_VP6F;
- break;
- default:
- buf->type = BUF_VIDEO_UNKNOWN;
- break;
- }
- buf->size = sizeof(xine_bmiheader);
- bih = (xine_bmiheader *) buf->content;
- memset(bih, 0, sizeof(xine_bmiheader));
- bih->biSize = sizeof(xine_bmiheader);
- bih->biWidth = this->width;
- bih->biHeight = this->height;
- if (buf->type == BUF_VIDEO_VP6F) {
- *((uint8_t *)buf->content+buf->size) = ((16-(this->width&15)) << 4) |
- ((16-(this->height&15)) & 0xf);
- bih->biSize++;
- buf->size++;
- }
- this->video_fifo->put(this->video_fifo, buf);
- this->got_video_header = 1;
- lprintf(" got video header from metadata...\n");
+ case FLV_TAG_TYPE_NOTIFY:
+ if (!this->got_info) {
+ unsigned char *text;
+ this->input->seek (this->input, -1, SEEK_CUR);
+ remaining_bytes++;
+ text = malloc (remaining_bytes + 1); /* 1 more byte for possible string end */
+ if (!text || this->input->read (this->input, (char *)text, remaining_bytes) != remaining_bytes) {
+ free (text);
+ goto fail;
}
-
- return this->status;
+ xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, "demux_flv: stream info:\n");
+ parse_amf (this, text, remaining_bytes);
+ free (text);
+ return (this->status);
}
- /* no preview */
- this->input->seek(this->input, remaining_bytes, SEEK_CUR);
- continue;
-
+ /* fall through */
default:
lprintf(" skipping packet...\n");
- this->input->seek(this->input, remaining_bytes, SEEK_CUR);
- continue;
+ continue;
}
- while (remaining_bytes) {
- buf = fifo->buffer_pool_alloc(fifo);
+ /* send mpeg4 style headers in both normal and preview mode. This makes sure that
+ they get through before they are needed. And it supports multiple sequences per
+ stream (unless we seek too far). */
+ if (mp4header) {
+ buf = fifo->buffer_pool_alloc (fifo);
buf->type = buf_type;
-
- buf->extra_info->input_time = pts;
- if (this->input->get_length(this->input)) {
- buf->extra_info->input_normpos =
- (int)((double)this->input->get_current_pos(this->input) * 65535.0 / this->size);
+ buf->size = 0;
+ buf->decoder_flags = BUF_FLAG_SPECIAL|BUF_FLAG_HEADER;
+ buf->decoder_info[1] = BUF_SPECIAL_DECODER_CONFIG;
+ buf->decoder_info[2] = remaining_bytes > buf->max_size ?
+ buf->max_size : remaining_bytes;
+ buf->decoder_info_ptr[2] = buf->mem;
+ if ((this->input->read (this->input, (char *)buf->mem, buf->decoder_info[2]))
+ != buf->decoder_info[2]) {
+ buf->free_buffer (buf);
+ goto fail;
}
+ remaining_bytes -= buf->decoder_info[2];
+ if (remaining_bytes)
+ this->input->seek (this->input, remaining_bytes, SEEK_CUR);
+ remaining_bytes = 0;
+ fifo->put (fifo, buf);
+ break;
+ }
- if ((buf_type == BUF_VIDEO_H264 || buf_type == BUF_AUDIO_AAC) && extrabuffer[0] == 0) {
- /* AVC/AAC sequence header */
- buf->pts = 0;
- buf->size = 0;
-
- buf->decoder_flags = BUF_FLAG_SPECIAL | BUF_FLAG_HEADER;
- if (preview)
- buf->decoder_flags |= BUF_FLAG_PREVIEW;
-
- buf->decoder_info[1] = BUF_SPECIAL_DECODER_CONFIG;
- buf->decoder_info[2] = MIN(remaining_bytes, buf->max_size);
- buf->decoder_info_ptr[2] = buf->mem;
-
- if (this->input->read(this->input, buf->mem, buf->decoder_info[2]) != buf->decoder_info[2]) {
- buf->free_buffer(buf);
- this->status = DEMUX_FINISHED;
- break;
- }
+ /* fkip frame contents in preview mode */
+ if (preview) {
+ if (remaining_bytes)
+ this->input->seek (this->input, remaining_bytes, SEEK_CUR);
+ return this->status;
+ }
- if (remaining_bytes > buf->max_size) {
- xprintf(this->xine, XINE_VERBOSITY_LOG,
- _("sequence header too big (%u bytes)!\n"), remaining_bytes);
- this->input->seek(this->input, remaining_bytes-buf->max_size, SEEK_CUR);
- }
- remaining_bytes = 0;
- }
- else {
- buf->pts = (int64_t) pts * 90;
- if (!preview)
- check_newpts(this, buf->pts, (tag_type == FLV_TAG_TYPE_VIDEO));
-
- if (remaining_bytes > buf->max_size)
- buf->size = buf->max_size;
- else
- buf->size = remaining_bytes;
- remaining_bytes -= buf->size;
-
- buf->decoder_flags = buf_flags;
- if (preview)
- buf->decoder_flags |= BUF_FLAG_PREVIEW;
- if (!remaining_bytes)
- buf->decoder_flags |= BUF_FLAG_FRAME_END;
-
- if (this->input->read(this->input, buf->content, buf->size) != buf->size) {
- buf->free_buffer(buf);
- this->status = DEMUX_FINISHED;
- break;
- }
+ /* send frame contents */
+ buf_pts = (int64_t)pts * 90;
+ check_newpts (this, buf_pts, (tag_type == FLV_TAG_TYPE_VIDEO));
+ size = this->input->get_length (this->input);
+ if (size > 0) {
+ this->size = size;
+ normpos = (int64_t)this->input->get_current_pos (this->input) * 65535 / size;
+ }
+ while (remaining_bytes) {
+ buf = fifo->buffer_pool_alloc (fifo);
+ buf->type = buf_type;
+ buf->pts = buf_pts + ptsoffs;
+ buf->extra_info->input_time = pts;
+ if (size > 0)
+ buf->extra_info->input_normpos = normpos;
+ buf->size = remaining_bytes > buf->max_size ? buf->max_size : remaining_bytes;
+ remaining_bytes -= buf->size;
+ if (!remaining_bytes)
+ buf_flags |= BUF_FLAG_FRAME_END;
+ buf->decoder_flags = buf_flags;
+ buf_flags &= ~BUF_FLAG_FRAME_START;
+ if (this->input->read (this->input, (char *)buf->content, buf->size) != buf->size) {
+ buf->free_buffer (buf);
+ goto fail;
}
-
fifo->put(fifo, buf);
}
@@ -737,123 +783,162 @@ static int read_flv_packet(demux_flv_t *this, int preview) {
return this->status;
}
-static void seek_flv_file(demux_flv_t *this, off_t seek_pos, int seek_pts) {
- unsigned char buffer[16];
- unsigned int pts = this->cur_pts;
- int len = 0;
- int next_tag = 0;
- int do_rewind = (seek_pts < this->cur_pts);
- int i;
-
- lprintf(" seeking %s to %d...\n",
- do_rewind ? "backward" : "forward", seek_pts);
+static void seek_flv_file (demux_flv_t *this, off_t seek_pos, int seek_pts) {
+ int i;
+ /* we start where we are */
+ off_t pos1, pos2, size, used, found;
+ unsigned char buf[4096], *p1, *p2;
+ unsigned int now = 0, fpts = this->cur_pts, try;
+
+ size = this->input->get_length (this->input);
+ if (size > 0)
+ this->size = size;
+ found = this->input->get_current_pos (this->input) + 4;
+ if (!seek_pos && this->length)
+ pos2 = (uint64_t)size * seek_pts / this->length;
+ else
+ pos2 = (uint64_t)size * seek_pos / 65535;
+
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG, "demux_flv: seek (%u.%03u, %"PRId64")\n",
+ seek_pts / 1000, seek_pts % 1000, (int64_t)pos2);
+
+ /* force send newpts */
+ this->buf_flag_seek = 1;
+ /* neither fileposition nor time given, restart at beginning */
if (seek_pos == 0 && seek_pts == 0) {
this->input->seek(this->input, this->start, SEEK_SET);
this->cur_pts = 0;
+ this->zero_pts_count = 0;
return;
}
-
- if (this->index) {
- if (do_rewind) {
- for (i = this->num_indices-1; i > 0; i--) {
- if (this->index[i-1].pts < seek_pts)
- break;
- }
+
+ /* use file index for time based seek (if we got 1) */
+ if (seek_pts && this->index) {
+ flv_index_entry_t *x;
+ uint32_t a = 0, b, c = this->num_indices;
+ while (a + 1 < c) {
+ b = (a + c) >> 1;
+ if (this->index[b].pts <= seek_pts) a = b; else c = b;
}
- else {
- for (i = 0; i < (this->num_indices-1); i++) {
- if (this->index[i+1].pts > seek_pts)
- break;
+ x = &this->index[a];
+ if ((x->offset >= this->start + 4) && (x->offset + 15 < size)) {
+ this->input->seek (this->input, x->offset, SEEK_SET);
+ this->input->read (this->input, (char *)buf, 15);
+ if (!buf[8] && !buf[9] && !buf[10] && (
+ ((buf[0] == FLV_TAG_TYPE_VIDEO) && ((buf[11] >> 4) == 1)) ||
+ (buf[0] == FLV_TAG_TYPE_AUDIO)
+ )) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "demux_flv: seek_index (%u.%03u, %"PRId64")\n",
+ x->pts / 1000, x->pts % 1000, (int64_t)x->offset);
+ this->input->seek (this->input, x->offset - 4, SEEK_SET);
+ this->cur_pts = x->pts;
+ return;
}
}
-
- if (this->index[i].offset >= this->start+4) {
- lprintf(" seeking to index entry %d (pts:%u, offset:%u).\n",
- i, this->index[i].pts, this->index[i].offset);
-
- this->input->seek(this->input, this->index[i].offset-4, SEEK_SET);
- this->cur_pts = this->index[i].pts;
- }
+ xprintf (this->xine, XINE_VERBOSITY_LOG, _("demux_flv: Not using broken seek index.\n"));
+ }
+ /* Up to 4 zero pts are OK (2 AAC/AVC sequence headers, 2 av tags).
+ Otherwise, the file is non seekable. Try a size based seek. */
+ if (this->zero_pts_count > 8) {
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ _("demux_flv: This file is non seekable. A/V lag may occur.\n"
+ " Recommend fixing the file with some flvtool.\n"));
+ seek_pts = 0;
}
- else if (seek_pos && this->videocodec && abs(seek_pts-this->cur_pts) > 300000) {
- off_t pos, size;
-
- pos = this->input->get_current_pos(this->input);
- size = this->filesize ? : this->input->get_length(this->input);
- this->input->seek(this->input, (uint64_t)size * seek_pos / 65535, SEEK_SET);
- lprintf(" resyncing...\n");
-
- /* resync */
- for (i = 0; i < 200000; i++) {
- uint8_t buf[4];
- if (this->input->read(this->input, buf, 1) < 1) {
- this->status = DEMUX_FINISHED;
- return;
- }
- if (buf[0] == FLV_TAG_TYPE_VIDEO) {
- this->input->seek(this->input, 7, SEEK_CUR);
- if (this->input->read(this->input, buf, 4) < 4) {
- this->status = DEMUX_FINISHED;
- return;
- }
- /* check StreamID and CodecID */
- if ( _X_ME_32(buf) == ME_FOURCC(0, 0, 0, (this->videocodec | 0x10)) ) {
- this->input->seek(this->input, -16, SEEK_CUR);
- lprintf(" ...resynced after %d bytes\n", i);
- return;
- }
- this->input->seek(this->input, -11, SEEK_CUR);
+ /* step 1: phonebook search. Estimate file position, find next tag header,
+ check time, make better estimation, repeat */
+ for (try = 4; try && (!seek_pts || abs ((int)seek_pts - (int)fpts) > 800); try--) {
+ pos1 = found;
+ found = 0;
+ this->input->seek (this->input, pos2, SEEK_SET);
+ used = this->input->read (this->input, (char *)buf + 4096 - 12, 12);
+ for (i = 0; !found && (i < 50); i++) {
+ memcpy (buf, buf + 4096 - 12, 12);
+ used = this->input->read (this->input, (char *)buf + 12, 4096 - 12);
+ if (used <= 0) break;
+ p1 = buf;
+ p2 = buf + used + 12;
+ while (!found && (p1 + 11 < p2)) switch (*p1++) {
+ case FLV_TAG_TYPE_AUDIO:
+ if (p1[7] || p1[8] || p1[9] || ((p1[10] >> 4) != this->audiocodec)) continue;
+ found = pos2 + (p1 - 1 - buf);
+ break;
+ case FLV_TAG_TYPE_VIDEO:
+ if (p1[7] || p1[8] || p1[9] || ((p1[10] & 0x0f) != this->videocodec)) continue;
+ found = pos2 + (p1 - 1 - buf);
+ break;
}
+ pos2 += 4096 - 12;
}
-
- lprintf(" ...resync failed!\n");
- this->input->seek(this->input, pos, SEEK_SET);
+ if (found) {
+ fpts = gettimestamp (p1, 3);
+ if (seek_pts && fpts) pos2 = (uint64_t)found * seek_pts / fpts;
+ else try = 1;
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "demux_flv: seek_quick (%u.%03u, %"PRId64")\n",
+ fpts / 1000, fpts % 1000, (int64_t)found);
+ } else found = pos1;
}
- else if (seek_pts) {
- while (do_rewind ? (seek_pts < this->cur_pts) : (seek_pts > this->cur_pts)) {
- unsigned char tag_type;
- int data_size;
- int ptag_size;
-
- if (next_tag)
- this->input->seek(this->input, next_tag, SEEK_CUR);
-
- len = this->input->read(this->input, buffer, 16);
- if (len != 16) {
- len = (len < 0) ? 0 : len;
- break;
- }
- ptag_size = _X_BE_32(&buffer[0]);
- tag_type = buffer[4];
- data_size = _X_BE_24(&buffer[5]);
- pts = _X_BE_24(&buffer[8]) | (buffer[11] << 24);
-
- if (do_rewind) {
- if (!ptag_size)
- break; /* beginning of movie */
- next_tag = -(ptag_size + 16 + 4);
- }
- else {
- next_tag = data_size - 1;
+ /* step 2: Traverse towards the desired time */
+ if (seek_pts) {
+ pos1 = 0;
+ pos2 = found;
+ i = 0;
+ while (1) {
+ if (pos2 < this->start + 4) break;
+ this->input->seek (this->input, pos2 - 4, SEEK_SET);
+ if (this->input->read (this->input, (char *)buf, 16) != 16) break;
+ if ((buf[4] == FLV_TAG_TYPE_VIDEO) && ((buf[15] >> 4) == 1)) pos1 = pos2;
+ if ((now = gettimestamp (buf, 8)) == 0) break;
+ if (now >= seek_pts) {
+ if (i > 0) break;
+ if ((i = _X_BE_32 (buf)) == 0) break;
+ found = pos2;
+ pos2 -= i + 4;
+ i = -1;
+ } else {
+ if (i < 0) break;
+ pos2 += _X_BE_24 (&buf[5]) + 15;
+ i = 1;
}
+ }
+ if (pos1) found = pos1;
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "demux_flv: seek_traverse (%u.%03u, %"PRId64")\n",
+ now / 1000, now % 1000, (int64_t)(pos2));
+ }
- if (this->flags & FLV_FLAG_HAS_VIDEO) {
- /* sync to video key frame */
- if (tag_type != FLV_TAG_TYPE_VIDEO || (buffer[15] >> 4) != 0x01)
- continue;
- lprintf(" video keyframe found at %d...\n", pts);
- }
- this->cur_pts = pts;
+ /* Go back to previous keyframe */
+ if (this->videocodec) {
+ pos1 = pos2 = found;
+ found = 0;
+ while (1) {
+ if (pos2 < this->start + 4) break;
+ this->input->seek (this->input, pos2 - 4, SEEK_SET);
+ if (this->input->read (this->input, (char *)buf, 16) != 16) break;
+ if ((buf[4] == FLV_TAG_TYPE_VIDEO) && ((buf[15] >> 4) == 1)) {found = pos2; break;}
+ if ((i = _X_BE_32 (buf)) == 0) break;
+ pos2 -= i + 4;
}
+ if (found) {
+ now = gettimestamp (buf, 8);
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "demux_flv: seek_keyframe (%u.%03u, %"PRId64")\n",
+ now / 1000, now % 1000, (int64_t)found);
+ } else found = pos1;
+ }
- /* seek back to the beginning of the tag */
- this->input->seek(this->input, -len, SEEK_CUR);
+ /* we are there!! */
+ this->input->seek (this->input, found + 4, SEEK_SET);
+ this->input->read (this->input, (char *)buf, 4);
+ this->cur_pts = gettimestamp (buf, 0);
+ this->input->seek (this->input, found - 4, SEEK_SET);
- lprintf( " seeked to %d.\n", pts);
- }
+ return;
}
@@ -887,8 +972,8 @@ static void demux_flv_send_headers(demux_plugin_t *this_gen) {
for (i = 0; i < 20; i++) {
if (read_flv_packet(this, 1) != DEMUX_OK)
break;
- if (((this->flags & FLV_FLAG_HAS_VIDEO) && this->got_video_header) &&
- ((this->flags & FLV_FLAG_HAS_AUDIO) && this->got_audio_header)) {
+ if (((!(this->flags & FLV_FLAG_HAS_VIDEO)) || this->got_video_header) &&
+ ((!(this->flags & FLV_FLAG_HAS_AUDIO)) || this->got_audio_header)) {
lprintf(" headers sent...\n");
break;
}
@@ -902,21 +987,26 @@ static int demux_flv_seek (demux_plugin_t *this_gen,
this->status = DEMUX_OK;
- if (INPUT_IS_SEEKABLE(this->input)) {
- if (start_pos && !start_time) {
- if (this->length)
- start_time = (int64_t) this->length * start_pos / 65535;
- else if (this->index)
- start_time = this->index[(int)(start_pos * (this->num_indices-1) / 65535)].pts;
- }
+ /* if demux thread is not running, do some init stuff */
+ if (!playing) {
+ this->last_pts[0] = 0;
+ this->last_pts[1] = 0;
+ _x_demux_flush_engine(this->stream);
+ seek_flv_file(this, start_pos, start_time);
+ _x_demux_control_newpts (this->stream, 0, 0);
+ return (this->status);
+ }
+
+ if (start_pos && !start_time)
+ start_time = (int64_t) this->length * start_pos / 65535;
+ /* always allow initial seek (this, 0, 0, 1) after send_headers ().
+ It usually works at least due to xine input cache.
+ Even if not, no problem there. */
+ if ((!start_time && !start_pos) || INPUT_IS_SEEKABLE (this->input)) {
if (!this->length || start_time < this->length) {
+ _x_demux_flush_engine(this->stream);
seek_flv_file(this, start_pos, start_time);
-
- if (playing) {
- this->buf_flag_seek = 1;
- _x_demux_flush_engine(this->stream);
- }
}
}
@@ -956,7 +1046,7 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str
input_plugin_t *input) {
demux_flv_t *this;
- this = calloc(1, sizeof(demux_flv_t));
+ this = calloc(1, sizeof (demux_flv_t));
this->xine = stream->xine;
this->stream = stream;
this->input = input;
@@ -994,7 +1084,7 @@ static demux_plugin_t *open_plugin (demux_class_t *class_gen, xine_stream_t *str
static void *init_plugin (xine_t *xine, void *data) {
demux_flv_class_t *this;
- this = calloc(1, sizeof(demux_flv_class_t));
+ this = calloc(1, sizeof (demux_flv_class_t));
this->demux_class.open_plugin = open_plugin;
this->demux_class.description = N_("Flash Video file demux plugin");
@@ -1020,3 +1110,4 @@ const plugin_info_t xine_plugin_info[] EXPORTED = {
{ PLUGIN_DEMUX, 27, "flashvideo", XINE_VERSION_CODE, &demux_info_flv, init_plugin },
{ PLUGIN_NONE, 0, "", 0, NULL, NULL }
};
+
diff --git a/src/video_out/video_out_opengl2.c b/src/video_out/video_out_opengl2.c
index fed693af0..aadfca107 100644
--- a/src/video_out/video_out_opengl2.c
+++ b/src/video_out/video_out_opengl2.c
@@ -1158,7 +1158,10 @@ static void opengl2_draw_video_bilinear( opengl2_driver_t *that, int guiw, int g
static void opengl2_draw( opengl2_driver_t *that, opengl2_frame_t *frame )
{
- glXMakeCurrent( that->display, that->drawable, that->context );
+ if ( !glXMakeCurrent( that->display, that->drawable, that->context ) ) {
+ xprintf( that->xine, XINE_VERBOSITY_LOG, "video_out_opengl2: display unavailable for rendering\n" );
+ return;
+ }
if ( !opengl2_check_textures_size( that, frame->width, frame->height ) ) {
glXMakeCurrent( that->display, None, NULL );
diff --git a/src/xine-engine/video_out.c b/src/xine-engine/video_out.c
index 5a31e2240..7d92991a5 100644
--- a/src/xine-engine/video_out.c
+++ b/src/xine-engine/video_out.c
@@ -1364,21 +1364,11 @@ static void overlay_and_display_frame (vos_t *this,
pthread_mutex_unlock( &img->stream->current_extra_info_lock );
}
- if (this->overlay_source) {
- this->overlay_source->multiple_overlay_blend (this->overlay_source,
- vpts,
- this->driver, img,
- this->video_loop_running && this->overlay_enabled);
- }
-
- vo_grab_current_frame (this, img, vpts);
-
- this->driver->display_frame (this->driver, img);
-
- /*
- * Wake up xine_play if it's waiting for a frame
+ /* xine_play() may be called from a thread that has the display device locked
+ * (eg an X window event handler). If it is waiting for a frame we better wake
+ * it up _before_ we start displaying, or the first 10 seconds of video are lost.
*/
- if( this->last_frame->is_first ) {
+ if( img->is_first ) {
pthread_mutex_lock(&this->streams_lock);
for (ite = xine_list_front(this->streams); ite;
ite = xine_list_next(this->streams, ite)) {
@@ -1394,6 +1384,17 @@ static void overlay_and_display_frame (vos_t *this,
pthread_mutex_unlock(&this->streams_lock);
}
+ if (this->overlay_source) {
+ this->overlay_source->multiple_overlay_blend (this->overlay_source,
+ vpts,
+ this->driver, img,
+ this->video_loop_running && this->overlay_enabled);
+ }
+
+ vo_grab_current_frame (this, img, vpts);
+
+ this->driver->display_frame (this->driver, img);
+
this->redraw_needed = 0;
}