diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/combined/ffmpeg/ff_audio_decoder.c | 646 | ||||
-rw-r--r-- | src/combined/ffmpeg/ff_video_decoder.c | 121 | ||||
-rw-r--r-- | src/combined/ffmpeg/ffmpeg_compat.h | 4 | ||||
-rw-r--r-- | src/combined/ffmpeg/xine_video.list | 2 | ||||
-rw-r--r-- | src/demuxers/demux_flv.c | 1283 | ||||
-rw-r--r-- | src/video_out/video_out_opengl2.c | 5 | ||||
-rw-r--r-- | src/xine-engine/video_out.c | 29 |
7 files changed, 1301 insertions, 789 deletions
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; } |