From 8983adfcc90aed0d0f087765c66e24f6b3a5403a Mon Sep 17 00:00:00 2001 From: James Stembridge Date: Tue, 16 Mar 2004 23:31:30 +0000 Subject: By popular request, dv audio decoding CVS patchset: 6280 CVS date: 2004/03/16 23:31:30 --- src/libffmpeg/Makefile.am | 6 +- src/libffmpeg/audio_decoder.c | 5 +- src/libffmpeg/dvaudio_decoder.c | 430 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 436 insertions(+), 5 deletions(-) create mode 100644 src/libffmpeg/dvaudio_decoder.c (limited to 'src') diff --git a/src/libffmpeg/Makefile.am b/src/libffmpeg/Makefile.am index c65d83dc3..40b7d57aa 100644 --- a/src/libffmpeg/Makefile.am +++ b/src/libffmpeg/Makefile.am @@ -9,7 +9,7 @@ INTERNAL_DOCS = diff_to_ffmpeg_cvs.txt libdir = $(XINE_PLUGINDIR) -lib_LTLIBRARIES = xineplug_decode_ff.la +lib_LTLIBRARIES = xineplug_decode_ff.la xineplug_decode_dvaudio.la if HAVE_DXR3 AM_CPPFLAGS = -I$(top_srcdir)/src/dxr3 $(X_CFLAGS) @@ -27,4 +27,8 @@ xineplug_decode_ff_la_LIBADD = $(MLIB_LIBS) $(XINE_LIB) -lm $(ZLIB_LIBS) \ $(top_builddir)/src/libffmpeg/libavcodec/libavcodec.la \ $(top_builddir)/src/libffmpeg/libavcodec/libpostproc/libpostprocess.la +xineplug_decode_dvaudio_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@ +xineplug_decode_dvaudio_la_SOURCES = dvaudio_decoder.c +xineplug_decode_dvaudio_la_LIBADD = $(XINE_LIB) + noinst_HEADERS = xine_decoder.h diff --git a/src/libffmpeg/audio_decoder.c b/src/libffmpeg/audio_decoder.c index 19b185ceb..26be7ce3f 100644 --- a/src/libffmpeg/audio_decoder.c +++ b/src/libffmpeg/audio_decoder.c @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: audio_decoder.c,v 1.7 2004/03/16 14:12:03 mroi Exp $ + * $Id: audio_decoder.c,v 1.8 2004/03/16 23:31:30 jstembridge Exp $ * * xine audio decoder plugin using ffmpeg * @@ -78,8 +78,6 @@ typedef struct ff_audio_decoder_s { static const ff_codec_t ff_audio_lookup[] = { {BUF_AUDIO_WMAV1, CODEC_ID_WMAV1, "MS Windows Media Audio 1 (ffmpeg)"}, {BUF_AUDIO_WMAV2, CODEC_ID_WMAV2, "MS Windows Media Audio 2 (ffmpeg)"}, - /* FIXME DV Audio has disappeared from libffmpeg - {BUD_AUDIO_DV, CODEC_ID_DVAUDIO, "DV Audio (ffmpeg)"}, */ {BUF_AUDIO_14_4, CODEC_ID_RA_144, "Real 14.4 (ffmpeg)"}, {BUF_AUDIO_28_8, CODEC_ID_RA_288, "Real 28.8 (ffmpeg)"}, {BUF_AUDIO_MPEG, CODEC_ID_MP3, "MP3 (ffmpeg)"}, @@ -397,7 +395,6 @@ void *init_audio_plugin (xine_t *xine, void *data) { static uint32_t supported_audio_types[] = { BUF_AUDIO_WMAV1, BUF_AUDIO_WMAV2, - BUF_AUDIO_DV, BUF_AUDIO_14_4, BUF_AUDIO_28_8, BUF_AUDIO_MULAW, diff --git a/src/libffmpeg/dvaudio_decoder.c b/src/libffmpeg/dvaudio_decoder.c new file mode 100644 index 000000000..1fb097a9c --- /dev/null +++ b/src/libffmpeg/dvaudio_decoder.c @@ -0,0 +1,430 @@ +/* + * Copyright (C) 2004 the xine project + * + * This file is part of xine, a free video player. + * + * xine is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * $Id: dvaudio_decoder.c,v 1.1 2004/03/16 23:31:30 jstembridge Exp $ + * + * dv audio decoder based on patch by Dan Dennedy + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#define LOG_MODULE "dvaudio" +#define LOG_VERBOSE +/* +#define LOG +*/ + +#include "xine_internal.h" +#include "buffer.h" +#include "xineutils.h" + +#ifdef _MSC_VER +/* ffmpeg has own definitions of those types */ +# undef int8_t +# undef uint8_t +# undef int16_t +# undef uint16_t +# undef int32_t +# undef uint32_t +# undef int64_t +# undef uint64_t +#endif + +#include "libavcodec/avcodec.h" +#include "libavcodec/dvdata.h" + +#ifdef _MSC_VER +# undef malloc +# undef free +# undef realloc +#endif + +#define AUDIOBUFSIZE 128*1024 +#define MAXFRAMESIZE 131072 + + +typedef struct { + audio_decoder_class_t decoder_class; +} dvaudio_class_t; + +typedef struct dvaudio_decoder_s { + audio_decoder_t audio_decoder; + + xine_stream_t *stream; + + int output_open; + int audio_channels; + int audio_bits; + int audio_sample_rate; + + unsigned char *buf; + int bufsize; + int size; + + char *decode_buffer; + int decoder_ok; + +} dvaudio_decoder_t; + + +enum dv_pack_type { + dv_header525 = 0x3f, /* see dv_write_pack for important details on */ + dv_header625 = 0xbf, /* these two packs */ + dv_timecode = 0x13, + dv_audio_source = 0x50, + dv_audio_control = 0x51, + dv_audio_recdate = 0x52, + dv_audio_rectime = 0x53, + dv_video_source = 0x60, + dv_video_control = 0x61, + dv_viedo_recdate = 0x62, + dv_video_rectime = 0x63, + dv_unknown_pack = 0xff, +}; + + +/* + * This is the dumbest implementation of all -- it simply looks at + * a fixed offset and if pack isn't there -- fails. We might want + * to have a fallback mechanism for complete search of missing packs. + */ +static const uint8_t* dv_extract_pack(uint8_t* frame, enum dv_pack_type t) +{ + int offs; + + switch (t) { + case dv_audio_source: + offs = (80*6 + 80*16*3 + 3); + break; + case dv_audio_control: + offs = (80*6 + 80*16*4 + 3); + break; + case dv_video_control: + offs = (80*5 + 48 + 5); + break; + default: + return NULL; + } + + return (frame[offs] == t ? &frame[offs] : NULL); +} + +static inline uint16_t dv_audio_12to16(uint16_t sample) +{ + uint16_t shift, result; + + sample = (sample < 0x800) ? sample : sample | 0xf000; + shift = (sample & 0xf00) >> 8; + + if (shift < 0x2 || shift > 0xd) { + result = sample; + } else if (shift < 0x8) { + shift--; + result = (sample - (256 * shift)) << shift; + } else { + shift = 0xe - shift; + result = ((sample + ((256 * shift) + 1)) << shift) - 1; + } + + return result; +} + +/* + * There's a couple of assumptions being made here: + * 1. By default we silence erroneous (0x8000/16bit 0x800/12bit) audio samples. + * We can pass them upwards when ffmpeg will be ready to deal with them. + * 2. We don't do software emphasis. + * 3. Audio is always returned as 16bit linear samples: 12bit nonlinear samples + * are converted into 16bit linear ones. + */ +static int dv_extract_audio(uint8_t* frame, uint8_t* pcm, uint8_t* pcm2) +{ + int size, i, j, d, of, smpls, freq, quant, half_ch; + uint16_t lc, rc; + const DVprofile* sys; + const uint8_t* as_pack; + + as_pack = dv_extract_pack(frame, dv_audio_source); + if (!as_pack) /* No audio ? */ + return 0; + + sys = dv_frame_profile(frame); + smpls = as_pack[1] & 0x3f; /* samples in this frame - min. samples */ + freq = (as_pack[4] >> 3) & 0x07; /* 0 - 48KHz, 1 - 44,1kHz, 2 - 32 kHz */ + quant = as_pack[4] & 0x07; /* 0 - 16bit linear, 1 - 12bit nonlinear */ + + if (quant > 1) + return -1; /* Unsupported quantization */ + + size = (sys->audio_min_samples[freq] + smpls) * 4; /* 2ch, 2bytes */ + half_ch = sys->difseg_size/2; + + /* for each DIF segment */ + for (i = 0; i < sys->difseg_size; i++) { + frame += 6 * 80; /* skip DIF segment header */ + if (quant == 1 && i == half_ch) { + if (!pcm2) + break; + else + pcm = pcm2; + } + + for (j = 0; j < 9; j++) { + for (d = 8; d < 80; d += 2) { + if (quant == 0) { /* 16bit quantization */ + of = sys->audio_shuffle[i][j] + (d - 8)/2 * sys->audio_stride; + if (of*2 >= size) + continue; + + pcm[of*2] = frame[d+1]; // FIXME: may be we have to admit + pcm[of*2+1] = frame[d]; // that DV is a big endian PCM + if (pcm[of*2+1] == 0x80 && pcm[of*2] == 0x00) + pcm[of*2+1] = 0; + } else { /* 12bit quantization */ + lc = ((uint16_t)frame[d] << 4) | + ((uint16_t)frame[d+2] >> 4); + rc = ((uint16_t)frame[d+1] << 4) | + ((uint16_t)frame[d+2] & 0x0f); + lc = (lc == 0x800 ? 0 : dv_audio_12to16(lc)); + rc = (rc == 0x800 ? 0 : dv_audio_12to16(rc)); + + of = sys->audio_shuffle[i%half_ch][j] + (d - 8)/3 * sys->audio_stride; + if (of*2 >= size) + continue; + + pcm[of*2] = lc & 0xff; // FIXME: may be we have to admit + pcm[of*2+1] = lc >> 8; // that DV is a big endian PCM + of = sys->audio_shuffle[i%half_ch+half_ch][j] + + (d - 8)/3 * sys->audio_stride; + pcm[of*2] = rc & 0xff; // FIXME: may be we have to admit + pcm[of*2+1] = rc >> 8; // that DV is a big endian PCM + ++d; + } + } + + frame += 16 * 80; /* 15 Video DIFs + 1 Audio DIF */ + } + } + + return size; +} + +static void dvaudio_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) { + + dvaudio_decoder_t *this = (dvaudio_decoder_t *) this_gen; + int bytes_consumed; + int decode_buffer_size; + int offset; + int out; + audio_buffer_t *audio_buffer; + int bytes_to_send; + + if (buf->decoder_flags & BUF_FLAG_PREVIEW) + return; + + if (buf->decoder_flags & BUF_FLAG_STDHEADER) { + this->buf = xine_xmalloc(AUDIOBUFSIZE); + this->bufsize = AUDIOBUFSIZE; + this->size = 0; + this->decode_buffer = xine_xmalloc(MAXFRAMESIZE); + + this->audio_sample_rate = buf->decoder_info[1]; + this->audio_bits = buf->decoder_info[2]; + this->audio_channels = buf->decoder_info[3]; + + _x_meta_info_set(this->stream, XINE_META_INFO_AUDIOCODEC, "DV Audio"); + + this->decoder_ok = 1; + + return; + } + + if (this->decoder_ok && !(buf->decoder_flags & (BUF_FLAG_HEADER|BUF_FLAG_SPECIAL))) { + + if (!this->output_open) { + this->output_open = this->stream->audio_out->open(this->stream->audio_out, + this->stream, this->audio_bits, this->audio_sample_rate, + (this->audio_channels == 2) ? AO_CAP_MODE_STEREO : AO_CAP_MODE_MONO); + } + + /* if the audio still isn't open, bail */ + if (!this->output_open) + return; + + if( this->size + buf->size > this->bufsize ) { + this->bufsize = this->size + 2 * buf->size; + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("dvaudio: increasing buffer to %d to avoid overflow.\n"), + this->bufsize); + this->buf = realloc( this->buf, this->bufsize ); + } + + xine_fast_memcpy (&this->buf[this->size], buf->content, buf->size); + this->size += buf->size; + + if (buf->decoder_flags & BUF_FLAG_FRAME_END) { /* time to decode a frame */ + + offset = 0; + while (this->size>0) { + decode_buffer_size = dv_extract_audio(&this->buf[offset], this->decode_buffer, NULL); + + if (decode_buffer_size > -1) + bytes_consumed = dv_frame_profile(&this->buf[offset])->frame_size; + else + bytes_consumed = decode_buffer_size; + + /* dispatch the decoded audio */ + out = 0; + while (out < decode_buffer_size) { + audio_buffer = + this->stream->audio_out->get_buffer (this->stream->audio_out); + if (audio_buffer->mem_size == 0) { + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, + "dvaudio: Help! Allocated audio buffer with nothing in it!\n"); + return; + } + + if ((decode_buffer_size - out) > audio_buffer->mem_size) + bytes_to_send = audio_buffer->mem_size; + else + bytes_to_send = decode_buffer_size - out; + + /* fill up this buffer */ + xine_fast_memcpy(audio_buffer->mem, &this->decode_buffer[out], + bytes_to_send); + /* byte count / 2 (bytes / sample) / channels */ + audio_buffer->num_frames = bytes_to_send / 2 / this->audio_channels; + + audio_buffer->vpts = buf->pts; + buf->pts = 0; /* only first buffer gets the real pts */ + this->stream->audio_out->put_buffer (this->stream->audio_out, + audio_buffer, this->stream); + + out += bytes_to_send; + } + + this->size -= bytes_consumed; + offset += bytes_consumed; + } + + /* reset internal accumulation buffer */ + this->size = 0; + } + } +} + +static void dvaudio_reset (audio_decoder_t *this_gen) { + dvaudio_decoder_t *this = (dvaudio_decoder_t *) this_gen; + + this->size = 0; +} + +static void dvaudio_discontinuity (audio_decoder_t *this_gen) { +} + +static void dvaudio_dispose (audio_decoder_t *this_gen) { + + dvaudio_decoder_t *this = (dvaudio_decoder_t *) this_gen; + + if (this->output_open) + this->stream->audio_out->close (this->stream->audio_out, this->stream); + this->output_open = 0; + + free(this->buf); + free(this->decode_buffer); + + free (this_gen); +} + +static audio_decoder_t *dvaudio_open_plugin (audio_decoder_class_t *class_gen, xine_stream_t *stream) { + + dvaudio_decoder_t *this ; + + this = (dvaudio_decoder_t *) xine_xmalloc (sizeof (dvaudio_decoder_t)); + + this->audio_decoder.decode_data = dvaudio_decode_data; + this->audio_decoder.reset = dvaudio_reset; + this->audio_decoder.discontinuity = dvaudio_discontinuity; + this->audio_decoder.dispose = dvaudio_dispose; + + this->output_open = 0; + this->audio_channels = 0; + this->stream = stream; + this->buf = NULL; + this->size = 0; + this->decoder_ok = 0; + + return &this->audio_decoder; +} + +static char *dvaudio_get_identifier (audio_decoder_class_t *this) { + return "dv audio"; +} + +static char *dvaudio_get_description (audio_decoder_class_t *this) { + return "dv audio decoder plugin"; +} + +static void dvaudio_dispose_class (audio_decoder_class_t *this) { + free (this); +} + +static void *init_dvaudio_plugin (xine_t *xine, void *data) { + + dvaudio_class_t *this ; + + this = (dvaudio_class_t *) xine_xmalloc (sizeof (dvaudio_class_t)); + + this->decoder_class.open_plugin = dvaudio_open_plugin; + this->decoder_class.get_identifier = dvaudio_get_identifier; + this->decoder_class.get_description = dvaudio_get_description; + this->decoder_class.dispose = dvaudio_dispose_class; + + return this; +} + +static uint32_t supported_audio_types[] = { + BUF_AUDIO_DV, + 0 +}; + +decoder_info_t dec_info_dvaudio = { + supported_audio_types, /* supported types */ + 5 /* priority */ +}; + +/* + * exported plugin catalog entry + */ + +plugin_info_t xine_plugin_info[] = { + /* type, API, "name", version, special_info, init_function */ + { PLUGIN_AUDIO_DECODER, 15, "dvaudio", XINE_VERSION_CODE, &dec_info_dvaudio, init_dvaudio_plugin }, + { PLUGIN_NONE, 0, "", 0, NULL, NULL } +}; -- cgit v1.2.3