# HG changeset patch # User reimar # Date 1121015652 0 # Node ID 087142ef3a2d9358f87b881c30c2b363d790c3b6 # Parent bb6729801e1cce2e21fc20ac0089a4909c35d357 musepack demuxing and decoding support (demuxing is v7 bitstream only). diff -r bb6729801e1c -r 087142ef3a2d AUTHORS --- a/AUTHORS Sun Jul 10 17:07:40 2005 +0000 +++ b/AUTHORS Sun Jul 10 17:14:12 2005 +0000 @@ -210,6 +210,7 @@ * reworked live changing playback speed * made MMS over HTTP stream selection work and modified ASF header parsing * reimplemented ao_jack without bio2jack dependency + * musepack demuxer and decoder, based on Reza's patch Dolbeau, Romain * random AltiVec (PowerPC multimedia extensions) stuff @@ -371,6 +372,7 @@ * seeking in Real files without index * savage_vid VIDIX driver * miscellaneous small fixes and improvements + * first musepack decoding patch Jermann, Jonas (g0th) * manual page rewrite diff -r bb6729801e1c -r 087142ef3a2d ChangeLog --- a/ChangeLog Sun Jul 10 17:07:40 2005 +0000 +++ b/ChangeLog Sun Jul 10 17:14:12 2005 +0000 @@ -19,6 +19,9 @@ * Fraps video decoder via binary DLL * support for 8-bit PNGs with palette * support for dmb1 MJPEG files with ffmjpeg + * support for musepack audio (WARNING: when muxing into e.g. AVI + you will be able to seek, but you might get ear- and speaker-breaking + noises) Demuxers: * simultaneous audio capture and playback in MPlayer diff -r bb6729801e1c -r 087142ef3a2d Makefile --- a/Makefile Sun Jul 10 17:07:40 2005 +0000 +++ b/Makefile Sun Jul 10 17:14:12 2005 +0000 @@ -94,6 +94,7 @@ $(ALSA_LIB) \ $(XMMS_LIB) \ $(X264_LIB) \ + $(MUSEPACK_LIB) \ COMMON_LIBS = libmpcodecs/libmpcodecs.a \ $(W32_LIB) \ diff -r bb6729801e1c -r 087142ef3a2d configure --- a/configure Sun Jul 10 17:07:40 2005 +0000 +++ b/configure Sun Jul 10 17:14:12 2005 +0000 @@ -233,6 +233,7 @@ --disable-liba52 disable builtin liba52 [enabled] --enable-libdts enable libdts support [autodetect] --disable-libmpeg2 disable builtin libmpeg2 [enabled] + --disable-musepack disable musepack support [autodetect] --disable-amr_nb disable amr narrowband, floating point [autodetect] --disable-amr_nb-fixed disable amr narrowband, fixed point [autodetect] --disable-amr_wb disable amr wideband, floating point [autodetect] @@ -1476,6 +1477,7 @@ _inet6=auto _gethostbyname2=auto _ftp=yes +_musepack=auto _vstream=auto _pthreads=yes for ac_option do @@ -1595,6 +1597,8 @@ --disable-libdts) _libdts=no ;; --enable-libmpeg2) _libmpeg2=yes ;; --disable-libmpeg2) _libmpeg2=no ;; + --enable-musepack) _musepack=yes ;; + --disable-musepack) _musepack=no ;; --enable-internal-matroska) _matroska_internal=yes ;; --disable-internal-matroska) _matroska_internal=no ;; --enable-internal-faad) _faad_internal=yes _faad_external=no ;; @@ -5446,6 +5450,30 @@ fi echores "$_libmpeg2" +echocheck "patched libmpcdec (musepack)" +if test "$_musepack" = auto ; then + _musepack=no + cat > $TMPC << EOF +#include +int main(void) { + mpc_streaminfo info; + mpc_decoder decoder; + mpc_decoder_set_streaminfo(&decoder, &info); + mpc_decoder_decode_frame(&decoder, NULL, 0, NULL); +} +EOF + cc_check -lmpcdec $_ld_lm && _musepack=yes +fi +if test "$_musepack" = yes ; then + _def_musepack='#define HAVE_MUSEPACK 1' + _ld_musepack='-lmpcdec' + _codecmodules="musepack $_codecmodules" +else + _def_musepack='#undef HAVE_MUSEPACK' + _nocodecmodules="musepack $_nocodecmodules" +fi +echores "$_musepack" + echocheck "Matroska support" if test "$_matroska_internal" = yes ; then @@ -6899,6 +6927,8 @@ TREMOR = $_tremor_internal TREMOR_FLAGS = $_tremor_flags +MUSEPACK = $_musepack + UNRARLIB = $_unrarlib HAVE_FFPOSTPROCESS = $_def_haveffpostprocess PNG = $_mkf_png @@ -7019,6 +7049,7 @@ TOOLAME_LIB=$_toolame_lib TWOLAME=$_twolame TWOLAME_LIB=$_twolame_lib +MUSEPACK_LIB = $_ld_musepack FAAC=$_faac FAAC_LIB=$_ld_faac AMR_NB=$_amr_nb @@ -7559,6 +7590,9 @@ /* enable Tremor as vorbis decoder */ $_def_tremor +/* enable musepack support */ +$_def_musepack + /* enable OggTheora support */ $_def_theora diff -r bb6729801e1c -r 087142ef3a2d etc/codecs.conf --- a/etc/codecs.conf Sun Jul 10 17:07:40 2005 +0000 +++ b/etc/codecs.conf Sun Jul 10 17:14:12 2005 +0000 @@ -2479,6 +2479,13 @@ driver ffmpeg dll "dts" +audiocodec musepack + info "MPC/MpegPlus audio codec" + status working + fourcc "MPC " + format 0x2b4d + driver libmusepack + audiocodec ffamrnb info "AMR Narrowband" status working diff -r bb6729801e1c -r 087142ef3a2d libmpcodecs/Makefile --- a/libmpcodecs/Makefile Sun Jul 10 17:07:40 2005 +0000 +++ b/libmpcodecs/Makefile Sun Jul 10 17:14:12 2005 +0000 @@ -187,6 +187,10 @@ ENCODER_SRCS += ae_lavc.c endif +ifeq ($(MUSEPACK),yes) +AUDIO_SRCS += ad_mpc.c +endif + ifeq ($(FAAC),yes) ENCODER_SRCS += ae_faac.c endif diff -r bb6729801e1c -r 087142ef3a2d libmpcodecs/ad.c --- a/libmpcodecs/ad.c Sun Jul 10 17:07:40 2005 +0000 +++ b/libmpcodecs/ad.c Sun Jul 10 17:14:12 2005 +0000 @@ -39,6 +39,7 @@ extern ad_functions_t mpcodecs_ad_qtaudio; extern ad_functions_t mpcodecs_ad_ra1428; extern ad_functions_t mpcodecs_ad_twin; +extern ad_functions_t mpcodecs_ad_libmusepack; ad_functions_t* mpcodecs_ad_drivers[] = { @@ -87,5 +88,8 @@ &mpcodecs_ad_libdv, #endif &mpcodecs_ad_ra1428, +#ifdef HAVE_MUSEPACK + &mpcodecs_ad_libmusepack, +#endif NULL }; diff -r bb6729801e1c -r 087142ef3a2d libmpcodecs/ad_mpc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmpcodecs/ad_mpc.c Sun Jul 10 17:14:12 2005 +0000 @@ -0,0 +1,178 @@ +/** + * Musepack audio files decoder for MPlayer + * by Reza Jelveh and + * Reimar Döffinger + * License: GPL + */ + +#include +#include +#include + +#include "config.h" +#include "ad_internal.h" +#include "../libaf/af_format.h" +#include "../libvo/fastmemcpy.h" + +static ad_info_t info = +{ + "MPC/MPEGPlus audio decoder", + "libmusepack", + "Reza Jelveh and Reimar Döffinger", + "", + "" +}; + +LIBAD_EXTERN(libmusepack) + +#include + +#define MAX_FRAMESIZE MPC_DECODER_BUFFER_LENGTH + +typedef struct context_s { + char *header; + int header_len; + sh_audio_t *sh; + uint32_t pos; + mpc_decoder decoder; +} context_t; + +/** + * \brief mpc_reader callback function for reading the header + */ +static mpc_int32_t cb_read(void *data, void *buf, mpc_int32_t size) { + context_t *d = (context_t *)data; + char *p = (char *)buf; + int s = size; + if (d->pos < d->header_len) { + if (s > d->header_len - d->pos) + s = d->header_len - d->pos; + memcpy(p, &d->header[d->pos], s); + } else + s = 0; + memset(&p[s], 0, size - s); + d->pos += size; + return size; +} + +/** + * \brief dummy mpc_reader callback function for seeking + */ +static mpc_bool_t cb_seek(void *data, mpc_int32_t offset ) { + context_t *d = (context_t *)data; + d->pos = offset; + return 1; +} + +/** + * \brief dummy mpc_reader callback function for getting stream position + */ +static mpc_int32_t cb_tell(void *data) { + context_t *d = (context_t *)data; + return d->pos; +} + +/** + * \brief dummy mpc_reader callback function for getting stream length + */ +static mpc_int32_t cb_get_size(void *data) { + return 1 << 30; +} + +/** + * \brief mpc_reader callback function, we cannot seek. + */ +static mpc_bool_t cb_canseek(void *data) { + return 0; +} + + +mpc_reader header_reader = { + .read = cb_read, .seek = cb_seek, .tell = cb_tell, + .get_size = cb_get_size, .canseek = cb_canseek +}; + +static int preinit(sh_audio_t *sh) { +// sh->audio_out_minsize = 2 * MPC_DECODER_BUFFER_LENGTH; + return 1; +} + +static void uninit(sh_audio_t *sh) { + if (sh->context) + free(sh->context); +} + +static int init(sh_audio_t *sh) { + mpc_streaminfo info; + context_t *cd = malloc(sizeof(context_t)); + + if (!sh->wf || (sh->wf->cbSize < 6 * 4)) { + mp_msg(MSGT_DECAUDIO, MSGL_FATAL, "Missing extradata!\n"); + return 0; + } + cd->header = (char *)sh->wf; + cd->header = &cd->header[sizeof(WAVEFORMATEX)]; + cd->header_len = sh->wf->cbSize; + cd->sh = sh; + cd->pos = 0; + sh->context = (char *)cd; + + /* read file's streaminfo data */ + mpc_streaminfo_init(&info); + header_reader.data = cd; + if (mpc_streaminfo_read(&info, &header_reader) != ERROR_CODE_OK) { + mp_msg(MSGT_DECAUDIO, MSGL_FATAL, "Not a valid musepack file.\n"); + return 0; + } + sh->i_bps = info.average_bitrate / 8; + sh->channels = info.channels; + sh->samplerate = info.sample_freq; + sh->samplesize = 4; + sh->sample_format = +#if MPC_SAMPLE_FORMAT == float + AF_FORMAT_FLOAT_NE; +#elif MPC_SAMPLE_FORMAT == mpc_int32_t + AF_FORMAT_S32_NE; +#else + #error musepack lib must use either float or mpc_int32_t sample format +#endif + + mpc_decoder_setup(&cd->decoder, NULL); + mpc_decoder_set_streaminfo(&cd->decoder, &info); + return 1; +} + +// FIXME: minlen is currently ignored +static int decode_audio(sh_audio_t *sh, unsigned char *buf, + int minlen, int maxlen) { + int status, len; + MPC_SAMPLE_FORMAT *sample_buffer = (MPC_SAMPLE_FORMAT *)buf; + mpc_uint32_t *packet = NULL; + + context_t *cd = (context_t *) sh->context; + if (maxlen < MPC_DECODER_BUFFER_LENGTH) { + mp_msg(MSGT_DECAUDIO, MSGL_ERR, "maxlen too small in decode_audio\n"); + return -1; + } + len = ds_get_packet(sh->ds, (unsigned char **)&packet); + if (len <= 0) return -1; + status = mpc_decoder_decode_frame(&cd->decoder, packet, len, sample_buffer); + if (status == -1) // decode error + mp_msg(MSGT_DECAUDIO, MSGL_FATAL, "Error decoding file.\n"); + if (status <= 0) // error or EOF + return -1; + + status = MPC_FRAME_LENGTH * sh->channels; // one sample per channel +#if MPC_SAMPLE_FORMAT == float || MPC_SAMPLE_FORMAT == mpc_int32_t + status *= 4; +#else + // should not happen + status *= 2; +#endif + return status; +} + +static int control(sh_audio_t *sh, int cmd, void* arg, ...) { + return CONTROL_UNKNOWN; +} + diff -r bb6729801e1c -r 087142ef3a2d libmpdemux/Makefile --- a/libmpdemux/Makefile Sun Jul 10 17:07:40 2005 +0000 +++ b/libmpdemux/Makefile Sun Jul 10 17:14:12 2005 +0000 @@ -69,6 +69,7 @@ demux_lmlm4.c \ demux_mf.c \ demux_mov.c \ + demux_mpc.c \ demux_mpg.c \ demux_nsv.c \ demux_nuv.c \ diff -r bb6729801e1c -r 087142ef3a2d libmpdemux/demux_mpc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmpdemux/demux_mpc.c Sun Jul 10 17:14:12 2005 +0000 @@ -0,0 +1,145 @@ + +#include "config.h" + +#include +#include +#include +#include "mp_msg.h" +#include "bswap.h" +#include "stream.h" +#include "demuxer.h" +#include "stheader.h" + + +#define HDR_SIZE (6 * 4) + +typedef struct da_priv { + float last_pts; + uint32_t dword; + int pos; +} da_priv_t; + +extern void free_sh_audio(sh_audio_t* sh); + +static uint32_t get_bits(da_priv_t* priv, stream_t* s, int bits) { + uint32_t out = priv->dword; + uint32_t mask = (1 << bits) - 1; + priv->pos += bits; + if (priv->pos < 32) { + out >>= (32 - priv->pos); + } + else { + stream_read(s, (void *)&priv->dword, 4); + le2me_32(priv->dword); + priv->pos -= 32; + if (priv->pos) { + out <<= priv->pos; + out |= priv->dword >> (32 - priv->pos); + } + } + return out & mask; +} + +int demux_mpc_open(demuxer_t* demuxer) { + stream_t *s = demuxer->stream; + sh_audio_t* sh_audio; + uint8_t hdr[HDR_SIZE]; + da_priv_t* priv; + int i; + + if (stream_read(s, hdr, HDR_SIZE) != HDR_SIZE) + return 0; + for (i = 0; i < 30000 && !s->eof; i++) { + if (hdr[0] == 'M' && hdr[1] == 'P' && hdr[2] == '+') + break; + memmove(hdr, &hdr[1], HDR_SIZE - 1); + stream_read(s, &hdr[HDR_SIZE - 1], 1); + } + + if (hdr[0] != 'M' || hdr[1] != 'P' || hdr[2] != '+') + return 0; + + sh_audio = new_sh_audio(demuxer,0); + + { + char *wf = (char *)calloc(1, sizeof(WAVEFORMATEX) + HDR_SIZE); + char *header = &wf[sizeof(WAVEFORMATEX)]; + const int freqs[4] = {44100, 48000, 37800, 32000}; + sh_audio->format = mmioFOURCC('M', 'P', 'C', ' '); + memcpy(header, hdr, HDR_SIZE); + sh_audio->wf = (WAVEFORMATEX *)wf; + sh_audio->wf->wFormatTag = sh_audio->format; + sh_audio->wf->nChannels = 2; + sh_audio->wf->nSamplesPerSec = freqs[header[10] & 3]; + sh_audio->wf->nBlockAlign = 32 * 36; + sh_audio->wf->wBitsPerSample = 16; + sh_audio->wf->nAvgBytesPerSec = 128 * 1024; // dummy to make mencoder not hang + sh_audio->wf->cbSize = HDR_SIZE; + demuxer->movi_start = stream_tell(s); + demuxer->movi_end = s->end_pos; + } + + priv = (da_priv_t *)malloc(sizeof(da_priv_t)); + priv->last_pts = -1; + priv->dword = 0; + priv->pos = 0; + stream_read(s, (void *)&priv->dword, 4); + priv->pos = 8; + demuxer->priv = priv; + demuxer->audio->id = 0; + demuxer->audio->sh = sh_audio; + sh_audio->ds = demuxer->audio; + sh_audio->samplerate = sh_audio->wf->nSamplesPerSec; + sh_audio->i_bps = sh_audio->wf->nAvgBytesPerSec; + sh_audio->audio.dwSampleSize = 0; + sh_audio->audio.dwScale = 32 * 36; + sh_audio->audio.dwRate = sh_audio->samplerate; + + return 1; +} + +int demux_mpc_fill_buffer(demux_stream_t *ds) { + int l; + int bit_len; + demux_packet_t* dp; + sh_audio_t* sh_audio = ds->sh; + demuxer_t* demux = ds->demuxer; + da_priv_t* priv = demux->priv; + stream_t* s = demux->stream; + sh_audio = ds->sh; + + if (s->eof) + return 0; + + bit_len = get_bits(priv, s, 20); + dp = new_demux_packet((bit_len + 7) / 8); + for (l = 0; l < (bit_len / 8); l++) + dp->buffer[l] = get_bits(priv, s, 8); + bit_len %= 8; + if (bit_len) + dp->buffer[l] = get_bits(priv, s, bit_len) << (8 - bit_len); + if (priv->last_pts < 0) + priv->last_pts = 0; + else + priv->last_pts += (36 * 32) / (float)sh_audio->samplerate; + ds->pts = priv->last_pts - (ds_tell_pts(demux->audio) - + sh_audio->a_in_buffer_len)/(float)sh_audio->i_bps; + ds_add_packet(ds, dp); + return 1; +} + +void demux_mpc_seek(demuxer_t *demuxer,float rel_seek_secs,int flags){ +// TODO +} + +void demux_close_mpc(demuxer_t* demuxer) { + da_priv_t* priv = demuxer->priv; + + if(!priv) + return; + free(priv); +} + +int demux_mpc_control(demuxer_t *demuxer,int cmd, void *arg){ + return DEMUXER_CTRL_NOTIMPL; +} diff -r bb6729801e1c -r 087142ef3a2d libmpdemux/demuxer.c --- a/libmpdemux/demuxer.c Sun Jul 10 17:07:40 2005 +0000 +++ b/libmpdemux/demuxer.c Sun Jul 10 17:14:12 2005 +0000 @@ -140,6 +140,7 @@ extern void demux_close_nsv(demuxer_t* demuxer); extern void demux_close_nuv(demuxer_t* demuxer); extern void demux_close_audio(demuxer_t* demuxer); +extern void demux_close_mpc(demuxer_t* demuxer); extern void demux_close_ogg(demuxer_t* demuxer); extern void demux_close_mpg(demuxer_t* demuxer); extern void demux_close_rtp(demuxer_t* demuxer); @@ -211,6 +212,8 @@ #endif case DEMUXER_TYPE_AUDIO: demux_close_audio(demuxer); break; + case DEMUXER_TYPE_MPC: + demux_close_mpc(demuxer); break; #ifdef HAVE_OGGVORBIS case DEMUXER_TYPE_OGG: demux_close_ogg(demuxer); break; @@ -337,6 +340,7 @@ int demux_rawdv_fill_buffer(demuxer_t *demuxer); int demux_y4m_fill_buffer(demuxer_t *demux); int demux_audio_fill_buffer(demux_stream_t *ds); +int demux_mpc_fill_buffer(demux_stream_t *ds); int demux_pva_fill_buffer(demuxer_t *demux); int demux_xmms_fill_buffer(demuxer_t *demux,demux_stream_t *ds); int demux_gif_fill_buffer(demuxer_t *demux); @@ -388,6 +392,7 @@ #endif case DEMUXER_TYPE_Y4M: return demux_y4m_fill_buffer(demux); case DEMUXER_TYPE_AUDIO: return demux_audio_fill_buffer(ds); + case DEMUXER_TYPE_MPC: return demux_mpc_fill_buffer(ds); #ifdef HAVE_XMMS case DEMUXER_TYPE_XMMS: return demux_xmms_fill_buffer(demux,ds); #endif @@ -636,6 +641,7 @@ extern void demux_open_nsv(demuxer_t *demuxer); extern void demux_open_nuv(demuxer_t *demuxer); extern int demux_audio_open(demuxer_t* demuxer); +extern int demux_mpc_open(demuxer_t* demuxer); extern int demux_ogg_open(demuxer_t* demuxer); extern int demux_mpg_open(demuxer_t* demuxer); extern int demux_rawaudio_open(demuxer_t* demuxer); @@ -1063,6 +1069,17 @@ demuxer = NULL; } } +//=============== Try to open as musepack file: ================= +if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_MPC){ + demuxer=new_demuxer(stream,DEMUXER_TYPE_MPC,audio_id,video_id,dvdsub_id); + if(demux_mpc_open(demuxer)){ + mp_msg(MSGT_DEMUXER,MSGL_INFO,MSGTR_Detected_XXX_FileFormat, "MPC"); + file_format=DEMUXER_TYPE_MPC; + } else { + free_demuxer(demuxer); + demuxer = NULL; + } +} #ifdef HAVE_XMMS //=============== Try to open as XMMS file: ================= if(file_format==DEMUXER_TYPE_UNKNOWN || file_format==DEMUXER_TYPE_XMMS){ @@ -1446,6 +1463,7 @@ #endif extern void demux_audio_seek(demuxer_t *demuxer,float rel_seek_secs,int flags); +extern void demux_mpc_seek(demuxer_t *demuxer,float rel_seek_secs,int flags); extern void demux_demuxers_seek(demuxer_t *demuxer,float rel_seek_secs,int flags); extern void demux_ogg_seek(demuxer_t *demuxer,float rel_seek_secs,int flags); extern void demux_rawaudio_seek(demuxer_t *demuxer,float rel_seek_secs,int flags); @@ -1532,6 +1550,8 @@ demux_seek_nuv(demuxer,rel_seek_secs,flags); break; case DEMUXER_TYPE_AUDIO: demux_audio_seek(demuxer,rel_seek_secs,flags); break; + case DEMUXER_TYPE_MPC: + demux_mpc_seek(demuxer,rel_seek_secs,flags); break; case DEMUXER_TYPE_DEMUXERS: demux_demuxers_seek(demuxer,rel_seek_secs,flags); break; #ifdef HAVE_OGGVORBIS @@ -1635,6 +1655,7 @@ extern int demux_xmms_control(demuxer_t *demuxer, int cmd, void *arg); extern int demux_mkv_control(demuxer_t *demuxer, int cmd, void *arg); extern int demux_audio_control(demuxer_t *demuxer, int cmd, void *arg); +extern int demux_mpc_control(demuxer_t *demuxer, int cmd, void *arg); extern int demux_ogg_control(demuxer_t *demuxer, int cmd, void *arg); extern int demux_real_control(demuxer_t *demuxer, int cmd, void *arg); extern int demux_lavf_control(demuxer_t *demuxer, int cmd, void *arg); @@ -1663,6 +1684,8 @@ return demux_avi_control(demuxer,cmd,arg); case DEMUXER_TYPE_AUDIO: return demux_audio_control(demuxer,cmd,arg); + case DEMUXER_TYPE_MPC: + return demux_mpc_control(demuxer,cmd,arg); #ifdef HAVE_OGGVORBIS case DEMUXER_TYPE_OGG: return demux_ogg_control(demuxer,cmd,arg); diff -r bb6729801e1c -r 087142ef3a2d libmpdemux/demuxer.h --- a/libmpdemux/demuxer.h Sun Jul 10 17:07:40 2005 +0000 +++ b/libmpdemux/demuxer.h Sun Jul 10 17:14:12 2005 +0000 @@ -47,11 +47,12 @@ #define DEMUXER_TYPE_VQF 37 #define DEMUXER_TYPE_AVS 38 #define DEMUXER_TYPE_AAC 39 +#define DEMUXER_TYPE_MPC 40 // This should always match the higest demuxer type number. // Unless you want to disallow users to force the demuxer to some types #define DEMUXER_TYPE_MIN 0 -#define DEMUXER_TYPE_MAX 39 +#define DEMUXER_TYPE_MAX 40 #define DEMUXER_TYPE_DEMUXERS (1<<16) // A virtual demuxer type for the network code