From b5862770bdc41aa4d5501bf7cc3a620ef9d69bb6 Mon Sep 17 00:00:00 2001 From: Edgar Hucek Date: Thu, 4 Jul 2013 09:31:37 +0300 Subject: Updaed AUTHORS file --- AUTHORS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS b/AUTHORS index d4c757f3e..54f5e427c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -578,4 +578,7 @@ Christoph Pfister Albert Lee Solaris portability fixes (and other miscellaneous fixes) +Edgar Hucek + VAAPI Support + (let us know if we've forgotten anyone) -- cgit v1.2.3 From a771cb809c8f14057536f28f62930d32c2d4f088 Mon Sep 17 00:00:00 2001 From: Torsten Jager Date: Sat, 27 Jul 2013 16:10:12 +0300 Subject: Reinitialize VAAPI in get_buffer() when image size changes --- src/combined/ffmpeg/ff_video_decoder.c | 44 ++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/src/combined/ffmpeg/ff_video_decoder.c b/src/combined/ffmpeg/ff_video_decoder.c index 94a0d251d..662c52a71 100644 --- a/src/combined/ffmpeg/ff_video_decoder.c +++ b/src/combined/ffmpeg/ff_video_decoder.c @@ -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; @@ -257,6 +259,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, @@ -436,9 +458,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 +477,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); -- cgit v1.2.3 From e5318f7f8fa3c9b6f09fd9d32202a61f86edc1d8 Mon Sep 17 00:00:00 2001 From: Torsten Jager Date: Sat, 10 Aug 2013 23:21:55 +0200 Subject: ff_audio_decoder: fix multichannel playback * Observe channel configuration immediately after av_decode_audio* (). Do not try to access nonexistant channels after a 5.1 -> 2.0 switch for example. * Add NULL plane pointer paranoia. * Assume generic channel layout when no detailled one provided. Needed for wma2. * Follow user speaker arrangement changes on the fly. * Defer opening audio out until we have something to play. * Do not reopen audio out with identical settings. This and the previous item should help avoiding waiting on some drivers. * Hard wire output to int16_t. Some of the code did assume that, and we are converting to that anyway. * Do not read sample format from bits_per_coded_sample. Decoders neither alter that field, nor do they force its value to their output. * Rename some vars for better readability. --- src/combined/ffmpeg/ff_audio_decoder.c | 646 +++++++++++++++++++++++++-------- 1 file changed, 498 insertions(+), 148 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; -- cgit v1.2.3 From 76725a2eceff7360a565397b2f580e0d12063bf6 Mon Sep 17 00:00:00 2001 From: Torsten Jager Date: Thu, 15 Aug 2013 15:04:10 +0200 Subject: ffmpeg_video_decoder: default to square pixels Prevent vo loop from calculating undefined aspect ratio from _padded_ image size, leading to black bars and unnecessary scaling. --- src/combined/ffmpeg/ff_video_decoder.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/combined/ffmpeg/ff_video_decoder.c b/src/combined/ffmpeg/ff_video_decoder.c index 662c52a71..59a724faa 100644 --- a/src/combined/ffmpeg/ff_video_decoder.c +++ b/src/combined/ffmpeg/ff_video_decoder.c @@ -234,13 +234,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); -- cgit v1.2.3 From 5725ffb53951327d0667097f12f22976e771f003 Mon Sep 17 00:00:00 2001 From: Torsten Jager Date: Thu, 15 Aug 2013 15:04:25 +0200 Subject: ffmpeg_video_decoder: avoid CODEC_FLAG_EMU_EDGE libavcodec 54. 86.100 wmv2 and mpeg4 decoders ignore this flag (probably inside some dsp routine), provoking segfault. Turning off direct rendering is a quick but nasty workaround. If vo plugin can crop, we may drop that emulation without performance penalty, and sometimes even speed up a little. --- src/combined/ffmpeg/ff_video_decoder.c | 65 ++++++++++++++++++++++++---------- src/combined/ffmpeg/ffmpeg_compat.h | 4 +++ 2 files changed, 50 insertions(+), 19 deletions(-) diff --git a/src/combined/ffmpeg/ff_video_decoder.c b/src/combined/ffmpeg/ff_video_decoder.c index 59a724faa..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; @@ -227,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); @@ -327,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) { @@ -344,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, @@ -371,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]; @@ -379,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) */ @@ -549,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 */ @@ -611,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; } @@ -626,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; } } @@ -656,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 */ @@ -1486,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)) { @@ -1767,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 */ @@ -1781,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); @@ -1822,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 */ -- cgit v1.2.3 From 9b32b98fac1724ffa63bb29428abedfb1830a5d6 Mon Sep 17 00:00:00 2001 From: Torsten Jager Date: Thu, 22 Aug 2013 16:48:40 +0200 Subject: ffmpeg_video: use AV_CODEC_ID_MPEG2VIDEO avcodec.h says this should be preferred for both mpeg1 and 2. --- src/combined/ffmpeg/xine_video.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 -- cgit v1.2.3 From d197f2cb5d6982a05c2a56f27995dcbd14d2ef08 Mon Sep 17 00:00:00 2001 From: Torsten Jager Date: Thu, 22 Aug 2013 16:48:45 +0200 Subject: video_out_opengl2: skip rendering into invalid drawable Tested by provoking a Kaffeine segfault. --- src/video_out/video_out_opengl2.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 ); -- cgit v1.2.3 From 8245e421a68e424563ecf217153b4cddb7282dac Mon Sep 17 00:00:00 2001 From: Torsten Jager Date: Thu, 22 Aug 2013 16:48:50 +0200 Subject: AUTHORS update I really am a fan of yours :-) --- AUTHORS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/AUTHORS b/AUTHORS index 54f5e427c..87b126b0b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -578,6 +578,10 @@ Christoph Pfister Albert Lee Solaris portability fixes (and other miscellaneous fixes) +Christophe Thommeret + OpenGL 2.0 video out plugin, new VDPAU h264 decoder, + VDPAU bugfixes + Edgar Hucek VAAPI Support -- cgit v1.2.3 From f2d9657f5be7d58b67e7203e0e6549dcbe7e3fdb Mon Sep 17 00:00:00 2001 From: Torsten Jager Date: Thu, 22 Aug 2013 16:48:55 +0200 Subject: README.dvb: add hint for Kaffeine users --- doc/README.dvb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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. -- cgit v1.2.3 From 9b35b0ffb21b25a60c95ffc71aa10082341beec3 Mon Sep 17 00:00:00 2001 From: Torsten Jager Date: Thu, 22 Aug 2013 16:49:00 +0200 Subject: fixed another potential xine_play () hang 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. --- src/xine-engine/video_out.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) 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; } -- cgit v1.2.3 From d08771bdead811e8ccebe6ec6d93a4ededd10300 Mon Sep 17 00:00:00 2001 From: Torsten Jager Date: Tue, 3 Sep 2013 17:31:50 +0200 Subject: demux_flv rewrite Why? I use FLV a lot for editing. It is simple, and it is playable while writing. However, seeking was often terribly slow or non working. Reordered video (most h.264) also yielded some nasty unpredictable a/v lag, making music videos not much fun. And there are quite a few FLV files out there that do not follow all the standards. The flash browser plugin plays them normally, but xine liked to show strange malfunctions such as video collapsed to a horizontal line, or even crashed. How? This is the first file I ever edited in xine-lib. I started years ago, and I did not keep track of all the intermediate states. So please apoplogize me pushing all in one go. It wont happen again. Promised. What? * Overrun-safe iterative metainfo parser that tolerates at least most of the trash left by various "injector tools". * Skip obviously truncated tags. * Try to find the reliable settings in contradiction. * Send pts not dts for reordered (b-framed) video. * Large file support >= 2Gb on 32bit systems. * Fast time-based seek routine for files with working, damaged and no keyframe index. * Major optimizations. Less reads, and a lot less seeks. * More codecs. --- src/demuxers/demux_flv.c | 1283 +++++++++++++++++++++++++--------------------- 1 file changed, 687 insertions(+), 596 deletions(-) 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 #include #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<>(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 } }; + -- cgit v1.2.3