diff --git a/configure.ac b/configure.ac index 259522535..78625149c 100644 --- a/configure.ac +++ b/configure.ac @@ -27,55 +27,56 @@ if COMPILER=`$CC --version | head -n 1`; then AC_DEFINE_UNQUOTED(USED_COMPILER, ["$COMPILER"]) fi -AC_ARG_WITH([tremor], [AS_HELP_STRING([--with-tremor], - [use libvorbisidec instead of libogg/libvorbis])], - [TREMOR="$withval"], - [TREMOR=no]) - -AC_ARG_WITH([tremor-static], [AS_HELP_STRING([--with-tremor-static], - [use statically linked libvorbisidec instead of libogg/libvorbis])], - [TREMOR_STATIC="$withval"], - [TREMOR_STATIC=no]) +#AC_ARG_WITH([tremor], [AS_HELP_STRING([--with-tremor], +# [use libvorbisidec instead of libogg/libvorbis])], +# [TREMOR="$withval"], +# [TREMOR=no]) +# +#AC_ARG_WITH([tremor-static], [AS_HELP_STRING([--with-tremor-static], +# [use statically linked libvorbisidec instead of libogg/libvorbis])], +# [TREMOR_STATIC="$withval"], +# [TREMOR_STATIC=no]) if test "$BOXTYPE" = "tripledragon"; then TUXBOX_APPS_LIB_PKGCONFIG(DIRECTFB, directfb) fi -if test "$TREMOR_STATIC" = "yes"; then - TREMOR=yes -fi - -if test "$TREMOR" = "yes"; then - TUXBOX_APPS_LIB_PKGCONFIG(VORBISIDEC,vorbisidec) - AC_DEFINE(USE_TREMOR,1,use libvorbisidec/tremor library) -fi - -if test "$TREMOR_STATIC" = "yes"; then - # hack to get the static lib location from the pkg-config data - VORBISIDEC_LIBS="$(echo $VORBISIDEC_LIBS | sed 's@-L@@; s@ -l.*@/libvorbisidec.a@;')" -fi - -AM_CONDITIONAL(USE_TREMOR, test "$TREMOR" = "yes") +#if test "$TREMOR_STATIC" = "yes"; then +# TREMOR=yes +#fi +# +#if test "$TREMOR" = "yes"; then +# TUXBOX_APPS_LIB_PKGCONFIG(VORBISIDEC,vorbisidec) +# AC_DEFINE(USE_TREMOR,1,use libvorbisidec/tremor library) +#fi +# +#if test "$TREMOR_STATIC" = "yes"; then +# # hack to get the static lib location from the pkg-config data +# VORBISIDEC_LIBS="$(echo $VORBISIDEC_LIBS | sed 's@-L@@; s@ -l.*@/libvorbisidec.a@;')" +#fi +# +#AM_CONDITIONAL(USE_TREMOR, test "$TREMOR" = "yes") TUXBOX_APPS_LIB_CONFIG(CURL,curl-config) TUXBOX_APPS_LIB_CONFIG(FREETYPE,freetype-config) # TUXBOX_APPS_LIB_PKGCONFIG(OPENSSL,openssl) -TUXBOX_APPS_LIB_PKGCONFIG_CHECK(ID3TAG,libid3tag) -if test -z "$ID3TAG_CFLAGS" ; then - TUXBOX_APPS_LIB_PKGCONFIG(ID3TAG,id3tag) -fi -TUXBOX_APPS_LIB_PKGCONFIG_CHECK(MAD,libmad) -if test -z "$MAD_CFLAGS" ; then - TUXBOX_APPS_LIB_PKGCONFIG(MAD,mad) -fi -TUXBOX_APPS_LIB_PKGCONFIG_CHECK(OGG,ogg) -if test -z "$OGG_CFLAGS" ; then - TUXBOX_APPS_LIB_PKGCONFIG(OGG,ogg) -fi +#TUXBOX_APPS_LIB_PKGCONFIG_CHECK(ID3TAG,libid3tag) +#if test x"$ID3TAG_EXISTS" != xyes; then +# TUXBOX_APPS_LIB_PKGCONFIG(ID3TAG,id3tag) +#fi +#TUXBOX_APPS_LIB_PKGCONFIG_CHECK(MAD,libmad) +#if test x"$MAD_EXISTS" != xyes; then +# TUXBOX_APPS_LIB_PKGCONFIG(MAD,mad) +#fi +#TUXBOX_APPS_LIB_PKGCONFIG_CHECK(OGG,ogg) +#if test -z "$OGG_CFLAGS" ; then +# TUXBOX_APPS_LIB_PKGCONFIG(OGG,ogg) +#fi TUXBOX_APPS_LIB_PKGCONFIG(PNG,libpng) TUXBOX_APPS_LIB_PKGCONFIG(AVFORMAT,libavformat) TUXBOX_APPS_LIB_PKGCONFIG(AVCODEC,libavcodec) TUXBOX_APPS_LIB_PKGCONFIG(AVUTIL,libavutil) +TUXBOX_APPS_LIB_PKGCONFIG(SWRESAMPLE,libswresample) #TUXBOX_APPS_LIB_PKGCONFIG(CONFIGFILE,tuxbox-configfile) #TUXBOX_APPS_LIB_PKGCONFIG(CONNECTION,tuxbox-connection) #TUXBOX_APPS_LIB_PKGCONFIG(EVENTSERVER,tuxbox-eventserver) diff --git a/src/Makefile.am b/src/Makefile.am index 5d1b40ff3..4f27c1cb8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -35,25 +35,12 @@ SUBDIRS += lcddisplay AM_CPPFLAGS += -I$(top_srcdir)/lib/libtriple endif -if USE_TREMOR -VORBISLIBS = @VORBISIDEC_LIBS@ -else -VORBISLIBS = -lvorbisfile -lvorbis -logg -endif - -bin_PROGRAMS = neutrino +bin_PROGRAMS = neutrino rcsim neutrino_SOURCES = neutrino_menue.cpp neutrino.cpp AM_CPPFLAGS += -D_FILE_OFFSET_BITS=64 -if ENABLE_FLAC -FLACLIBS = -lFLAC -AM_CPPFLAGS += -DENABLE_FLAC -else -FLACLIBS = -endif - if BOXMODEL_APOLLO MTDUTILSLIBS = \ system/mtdutils/libneutrino_system_mtdutils.a \ @@ -99,14 +86,11 @@ neutrino_LDADD = \ @CURL_LIBS@ \ @FREETYPE_LIBS@ \ @PNG_LIBS@ \ - @MAD_LIBS@ \ - @ID3TAG_LIBS@ \ @LIBCS_LIBS@ \ @AVFORMAT_LIBS@ \ @AVUTIL_LIBS@ \ @AVCODEC_LIBS@ \ - $(FLACLIBS) \ - $(VORBISLIBS) \ + @SWRESAMPLE_LIBS@ \ -ldvbsi++ \ -ljpeg \ -lOpenThreads \ diff --git a/src/driver/audiodec/Makefile.am b/src/driver/audiodec/Makefile.am index a837cd020..16819b320 100644 --- a/src/driver/audiodec/Makefile.am +++ b/src/driver/audiodec/Makefile.am @@ -8,8 +8,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/lib \ -I$(top_srcdir)/src/zapit/include \ -I$(top_srcdir)/lib/libconfigfile \ - @FREETYPE_CFLAGS@ \ - @VORBISIDEC_CFLAGS@ + @FREETYPE_CFLAGS@ if BOXTYPE_COOL if BOXMODEL_APOLLO @@ -24,18 +23,6 @@ endif noinst_LIBRARIES = libneutrino_driver_audiodec.a -if ENABLE_FLAC -FLACdec= flacdec.cpp -endif - libneutrino_driver_audiodec_a_SOURCES = \ basedec.cpp \ - cdrdec.cpp \ - crc.c \ - ffmpegdec.cpp \ - $(FLACdec) \ - mp3dec.cpp \ - oggdec.cpp \ - tag.c \ - vis.cpp \ - wavdec.cpp + ffmpegdec.cpp diff --git a/src/driver/audiodec/basedec.cpp b/src/driver/audiodec/basedec.cpp index 88d0746e2..986574799 100644 --- a/src/driver/audiodec/basedec.cpp +++ b/src/driver/audiodec/basedec.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include // for ShoutcastCallback() @@ -42,14 +43,13 @@ #include #include "basedec.h" -#include "cdrdec.h" -#include "mp3dec.h" -#include "oggdec.h" -#include "wavdec.h" #include "ffmpegdec.h" + #include unsigned int CBaseDec::mSamplerate=0; +OpenThreads::Mutex CBaseDec::metaDataMutex; +std::map CBaseDec::metaDataCache; void ShoutcastCallback(void *arg) { @@ -80,140 +80,100 @@ CBaseDec::RetCode CBaseDec::DecoderBase(CAudiofile* const in, if ( Status == OK ) { + CFile::FileType ft; if( in->FileType == CFile::STREAM_AUDIO ) { if ( fstatus( fp, ShoutcastCallback ) < 0 ) { - fprintf( stderr, "Error adding shoutcast callback: %s", - err_txt ); + fprintf( stderr, "Error adding shoutcast callback: %s", err_txt ); } + if (ftype(fp, "ogg")) - { - Status = COggDec::getInstance()->Decoder( fp, OutputFd, state, - &in->MetaData, t, - secondsToSkip ); - } + ft = CFile::FILE_OGG; else if (ftype(fp, "mpeg")) - { - Status = CMP3Dec::getInstance()->Decoder( fp, OutputFd, state, - &in->MetaData, t, - secondsToSkip ); - } + ft = CFile::FILE_MP3; else - { - Status = CFfmpegDec::getInstance()->Decoder( fp, OutputFd, state, - &in->MetaData, t, - secondsToSkip ); - } + ft = CFile::FILE_UNKNOWN; } - else if( in->FileType == CFile::FILE_MP3) - { - Status = CMP3Dec::getInstance()->Decoder( fp, OutputFd, state, - &in->MetaData, t, - secondsToSkip ); - } - else if( in->FileType == CFile::FILE_OGG ) - { - Status = COggDec::getInstance()->Decoder( fp, OutputFd, state, - &in->MetaData, t, - secondsToSkip ); - } - else if( in->FileType == CFile::FILE_WAV ) - { - Status = CWavDec::getInstance()->Decoder( fp, OutputFd, state, - &in->MetaData, t, - secondsToSkip ); - } - else if( in->FileType == CFile::FILE_CDR ) - { - Status = CCdrDec::getInstance()->Decoder( fp, OutputFd, state, - &in->MetaData, t, - secondsToSkip ); - } -#ifdef ENABLE_FLAC - else if (in->FileType == CFile::FILE_FLAC) - { - Status = CFlacDec::getInstance()->Decoder(fp, OutputFd, state, - &in->MetaData, t, - secondsToSkip ); - } -#endif else { - Status = CFfmpegDec::getInstance()->Decoder(fp, OutputFd, state, - &in->MetaData, t, - secondsToSkip ); + struct stat st; + if (!fstat(fileno(fp), &st)) + in->MetaData.filesize = st.st_size; + + ft = in->FileType; } + Status = CFfmpegDec::getInstance()->Decoder(fp, ft, OutputFd, state, &in->MetaData, t, secondsToSkip ); + if ( fclose( fp ) == EOF ) { - fprintf( stderr, "Could not close file %s.\n", - in->Filename.c_str() ); + fprintf( stderr, "Could not close file %s.\n", in->Filename.c_str() ); } } return Status; } +bool CBaseDec::LookupMetaData(CAudiofile* const in) +{ + bool res = false; + metaDataMutex.lock(); + std::map::const_iterator it = metaDataCache.find(in->Filename); + if (it != metaDataCache.end()) { + *in = it->second; + res = true; + } + metaDataMutex.unlock(); + return res; +} + +void CBaseDec::CacheMetaData(CAudiofile* const in) +{ + metaDataMutex.lock(); + // FIXME: This places a limit on the cache size. A LRU scheme would be more appropriate. + if (metaDataCache.size() > 128) + metaDataCache.clear(); + metaDataCache[in->Filename] = *in; + metaDataMutex.unlock(); +} + +void CBaseDec::ClearMetaData() +{ + metaDataMutex.lock(); + metaDataCache.clear(); + metaDataMutex.unlock(); +} + bool CBaseDec::GetMetaDataBase(CAudiofile* const in, const bool nice) { - bool Status = true; + if (LookupMetaData(in)) + return true; - if (in->FileType == CFile::FILE_MP3 || in->FileType == CFile::FILE_OGG - || in->FileType == CFile::FILE_WAV || in->FileType == CFile::FILE_CDR -#ifdef ENABLE_FLAC - || in->FileType == CFile::FILE_FLAC -#endif - ) + bool Status = true; + FILE* fp = fopen( in->Filename.c_str(), "r" ); + if ( fp == NULL ) { - FILE* fp = fopen( in->Filename.c_str(), "r" ); - if ( fp == NULL ) - { - fprintf( stderr, "Error opening file %s for meta data reading.\n", - in->Filename.c_str() ); - Status = false; - } - else - { - if(in->FileType == CFile::FILE_MP3) - { - Status = CMP3Dec::getInstance()->GetMetaData(fp, nice, - &in->MetaData); - } - else if(in->FileType == CFile::FILE_OGG) - { - Status = COggDec::getInstance()->GetMetaData(fp, nice, - &in->MetaData); - } - else if(in->FileType == CFile::FILE_WAV) - { - Status = CWavDec::getInstance()->GetMetaData(fp, nice, - &in->MetaData); - } - else if(in->FileType == CFile::FILE_CDR) - { - Status = CCdrDec::getInstance()->GetMetaData(fp, nice, - &in->MetaData); - } -#ifdef ENABLE_FLAC - else if (in->FileType == CFile::FILE_FLAC) - { - CFlacDec FlacDec; - Status = FlacDec.GetMetaData(fp, nice, &in->MetaData); - } -#endif - if ( fclose( fp ) == EOF ) - { - fprintf( stderr, "Could not close file %s.\n", - in->Filename.c_str() ); - } - } + fprintf( stderr, "Error opening file %s for meta data reading.\n", + in->Filename.c_str() ); + Status = false; } else { - fprintf( stderr, "GetMetaDataBase: Filetype is not supported for " ); - fprintf( stderr, "meta data reading.\n" ); - Status = false; + struct stat st; + if (!fstat(fileno(fp), &st)) + in->MetaData.filesize = st.st_size; + + CFfmpegDec d; + Status = d.GetMetaData(fp, in->FileType, nice, &in->MetaData); + if (Status) + CacheMetaData(in); + + if ( fclose( fp ) == EOF ) + { + fprintf( stderr, "Could not close file %s.\n", + in->Filename.c_str() ); + } } return Status; diff --git a/src/driver/audiodec/basedec.h b/src/driver/audiodec/basedec.h index 5715e3e7f..a582facd3 100644 --- a/src/driver/audiodec/basedec.h +++ b/src/driver/audiodec/basedec.h @@ -32,6 +32,9 @@ #include #include #include +#include +#include +#include class CBaseDec { @@ -42,8 +45,8 @@ public: // the follwing two methods have to be implemented for new decoders //@param secondsToSkip: a value of 0 indicates that normal FF/REV operation was requested // a value > 0 indicates that *one* jump forwards (FF) or backwards (REV) was requested - virtual RetCode Decoder(FILE *, const int, State* const, CAudioMetaData*, time_t* const, unsigned int* const)=0; - virtual bool GetMetaData(FILE *in, const bool nice, CAudioMetaData* m)=0; + virtual RetCode Decoder(FILE *, const CFile::FileType ft, const int, State* const, CAudioMetaData*, time_t* const, unsigned int* const)=0; + virtual bool GetMetaData(FILE *in, const CFile::FileType ft, const bool nice, CAudioMetaData* m)=0; static RetCode DecoderBase(CAudiofile* const in, const int OutputFd, State* const state, time_t* const t, @@ -51,8 +54,14 @@ public: static bool GetMetaDataBase(CAudiofile* const in, const bool nice); static void Init(); - CBaseDec(){}; + CBaseDec() {}; static bool SetDSP(int soundfd, int fmt, unsigned int dsp_speed, unsigned int channels); + + static OpenThreads::Mutex metaDataMutex; + static std::map metaDataCache; + static void CacheMetaData(CAudiofile* const in); + static bool LookupMetaData(CAudiofile* const in); + static void ClearMetaData(); private: static bool avs_mute(bool mute); unsigned static int mSamplerate; diff --git a/src/driver/audiodec/ffmpegdec.cpp b/src/driver/audiodec/ffmpegdec.cpp index b6cf0a59a..e6522a338 100644 --- a/src/driver/audiodec/ffmpegdec.cpp +++ b/src/driver/audiodec/ffmpegdec.cpp @@ -39,16 +39,19 @@ extern "C" { #include #include } +#include + #include extern cAudio * audioDecoder; #define ProgName "FfmpegDec" -static void log_callback(void*, int, const char*format, va_list ap) +static OpenThreads::Mutex mutex; + +static void log_callback(void *, int, const char *format, va_list ap) { vfprintf(stderr, format, ap); - } CFfmpegDec::CFfmpegDec(void) @@ -58,26 +61,29 @@ CFfmpegDec::CFfmpegDec(void) buffer_size = 0x1000; buffer = NULL; avc = NULL; + avcodec_register_all(); + av_register_all(); } CFfmpegDec::~CFfmpegDec(void) { + DeInit(); } int CFfmpegDec::Read(void *buf, size_t buf_size) { - return fread(buf, buf_size, 1, (FILE *) in); + return (int) fread(buf, 1, buf_size, (FILE *) in); } static int read_packet(void *opaque, uint8_t *buf, int buf_size) { - int res = ((CFfmpegDec *) opaque)->Read(buf, (size_t) buf_size); - return res; + return ((CFfmpegDec *) opaque)->Read(buf, (size_t) buf_size); } int64_t CFfmpegDec::Seek(int64_t offset, int whence) { - return (int64_t) fseek((FILE *) in, (long) offset, whence); + fseek((FILE *) in, (long) offset, whence); + return (int64_t) ftell((FILE *) in); } static int64_t seek_packet(void *opaque, int64_t offset, int whence) @@ -85,36 +91,75 @@ static int64_t seek_packet(void *opaque, int64_t offset, int whence) return ((CFfmpegDec *) opaque)->Seek(offset, whence); } -bool CFfmpegDec::Init(void) +bool CFfmpegDec::Init(void *_in, const CFile::FileType ft) { - AVIOContext *avioc = NULL; - if (!avc) { - buffer = (unsigned char *) av_malloc(buffer_size); - if (!buffer) - return false; - avcodec_register_all(); - av_register_all(); - avc = avformat_alloc_context(); - if (!avc) - return false; + title = ""; + artist = ""; + date = ""; + album = ""; + genre = ""; + type_info = ""; + total_time = 0; + bitrate = 0; + total_time = 0; + AVIOContext *avioc = NULL; + in = _in; + is_stream = fseek((FILE *)in, 0, SEEK_SET); + buffer = (unsigned char *) av_malloc(buffer_size); + if (!buffer) + return false; + avc = avformat_alloc_context(); + if (!avc) { + av_freep(&buffer); + return false; + } + + if (is_stream) avc->probesize = 128 * 1024; - avioc = avio_alloc_context (buffer, buffer_size, 0, this, read_packet, NULL, seek_packet); - if (!avioc) { - av_freep(&buffer); - avformat_free_context(avc); - return false; - } - avc->pb = avioc; - avc->flags = AVFMT_FLAG_CUSTOM_IO; - if (!avformat_open_input(&avc, "", NULL, 0)) - return true; + avioc = avio_alloc_context (buffer, buffer_size, 0, this, read_packet, NULL, seek_packet); + if (!avioc) { + av_freep(&buffer); + avformat_free_context(avc); + return false; } - if (avioc) - av_freep(avioc); - avc = NULL; - return false; + avc->pb = avioc; + avc->flags |= AVFMT_FLAG_CUSTOM_IO|AVFMT_FLAG_KEEP_SIDE_DATA; + + AVInputFormat *input_format = NULL; + + switch (ft) { + case CFile::FILE_OGG: + input_format = av_find_input_format("vorbis"); + break; + case CFile::FILE_MP3: + input_format = av_find_input_format("mp3"); + break; + case CFile::FILE_WAV: + input_format = av_find_input_format("wav"); + break; + case CFile::FILE_FLAC: + input_format = av_find_input_format("flac"); + break; + default: + break; + } + + int r = avformat_open_input(&avc, "", input_format, NULL); + if (r) { + char buf[200]; av_strerror(r, buf, sizeof(buf)); + fprintf(stderr, "%d %s %d: %s\n", __LINE__, __func__,r,buf); + if (avioc) + av_freep(avioc); + if (avc) { + avformat_close_input(&avc); + avformat_free_context(avc); + avc = NULL; + } + return false; + } + return true; } void CFfmpegDec::DeInit(void) @@ -127,25 +172,18 @@ void CFfmpegDec::DeInit(void) avformat_free_context(avc); avc = NULL; } -#if 0 - if (buffer) { - av_freep(&buffer); - } -#endif +// if (buffer) +// av_freep(&buffer); + in = NULL; } -CBaseDec::RetCode CFfmpegDec::Decoder(FILE *_in, int /*OutputFd*/, State* state, CAudioMetaData* _meta_data, time_t* time_played, unsigned int* /*secondsToSkip*/) +CBaseDec::RetCode CFfmpegDec::Decoder(FILE *_in, const CFile::FileType ft, int /*OutputFd*/, State* state, CAudioMetaData* _meta_data, time_t* time_played, unsigned int* secondsToSkip) { in = _in; RetCode Status=OK; + is_stream = fseek((FILE *)in, 0, SEEK_SET); - if (!Init()) { - Status=DATA_ERR; - return Status; - } - - - if (!SetMetaData((FILE *)in, _meta_data)) { + if (!SetMetaData((FILE *)in, ft, _meta_data)) { DeInit(); Status=DATA_ERR; return Status; @@ -153,7 +191,10 @@ CBaseDec::RetCode CFfmpegDec::Decoder(FILE *_in, int /*OutputFd*/, State* state, AVCodecContext *c = avc->streams[best_stream]->codec; - if(avcodec_open2(c, codec, NULL)) + mutex.lock(); + int r = avcodec_open2(c, codec, NULL); + mutex.unlock(); + if (r) { DeInit(); Status=DATA_ERR; @@ -162,14 +203,15 @@ CBaseDec::RetCode CFfmpegDec::Decoder(FILE *_in, int /*OutputFd*/, State* state, SwrContext *swr = swr_alloc(); if (!swr) { + mutex.lock(); avcodec_close(c); + mutex.unlock(); DeInit(); Status=DATA_ERR; return Status; } - meta_data_valid = true; - + mSampleRate = samplerate; #if __BYTE_ORDER == __LITTLE_ENDIAN audioDecoder->PrepareClipPlay(mChannels, mSampleRate, 32, 1); #else @@ -193,11 +235,36 @@ CBaseDec::RetCode CFfmpegDec::Decoder(FILE *_in, int /*OutputFd*/, State* state, int outsamples = 0; int outsamples_max = 0; - time_t starttime = time(NULL); + int64_t pts = 0, start_pts = 0, next_skip_pts = 0; + uint64_t skip = 0; + int seek_flags = 0; + if (!is_stream && secondsToSkip && *secondsToSkip) + skip = avc->streams[best_stream]->time_base.num * *secondsToSkip / avc->streams[best_stream]->time_base.den; do { - while(*state==PAUSE) + if (!is_stream && (skip || *state==FF || *state==REV) && avc->streams[best_stream]->time_base.num) { + if (!next_skip_pts || pts >= next_skip_pts) { + skip = avc->streams[best_stream]->time_base.den / avc->streams[best_stream]->time_base.num; + if (*state == REV) { + next_skip_pts = pts - skip; + pts = next_skip_pts - skip/4; + seek_flags = AVSEEK_FLAG_BACKWARD; + if (pts < start_pts) { + pts = start_pts; + *state = PAUSE; + } + } else { + pts += skip; + next_skip_pts = pts + skip/4; + seek_flags = 0; + } + av_seek_frame(avc, best_stream, pts, seek_flags); + skip = 0; + } + } + + while(*state==PAUSE && !is_stream) usleep(10000); if (av_read_frame(avc, &packet)) { @@ -223,11 +290,13 @@ CBaseDec::RetCode CFfmpegDec::Decoder(FILE *_in, int /*OutputFd*/, State* state, // skip frame packet.size = 0; avcodec_flush_buffers(c); + mutex.lock(); avcodec_close(c); avcodec_open2(c, codec, NULL); + mutex.unlock(); continue; } - if (got_frame) { + if (got_frame && *state!=PAUSE) { int out_samples; outsamples = av_rescale_rnd(swr_get_delay(swr, c->sample_rate) + frame->nb_samples, c->sample_rate, c->sample_rate, AV_ROUND_UP); @@ -251,12 +320,15 @@ CBaseDec::RetCode CFfmpegDec::Decoder(FILE *_in, int /*OutputFd*/, State* state, fprintf(stderr,"%s: PCM write error (%s).\n", ProgName, strerror(errno)); Status=WRITE_ERR; } + pts = av_frame_get_best_effort_timestamp(frame); + if (!start_pts) + start_pts = pts; } packet.size -= len; packet.data += len; } - if (time_played) - *time_played = time(NULL) - starttime; + if (time_played && avc->streams[best_stream]->time_base.den) + *time_played = (pts - start_pts) * avc->streams[best_stream]->time_base.num / avc->streams[best_stream]->time_base.den; } while (*state!=STOP_REQ && Status==OK); audioDecoder->StopClip(); @@ -273,9 +345,9 @@ CBaseDec::RetCode CFfmpegDec::Decoder(FILE *_in, int /*OutputFd*/, State* state, return Status; } -bool CFfmpegDec::GetMetaData(FILE *_in, const bool /*nice*/, CAudioMetaData* m) +bool CFfmpegDec::GetMetaData(FILE *_in, const CFile::FileType ft, const bool /*nice*/, CAudioMetaData* m) { - return SetMetaData(_in, m); + return SetMetaData(_in, ft, m); } CFfmpegDec* CFfmpegDec::getInstance() @@ -288,38 +360,87 @@ CFfmpegDec* CFfmpegDec::getInstance() return FfmpegDec; } -bool CFfmpegDec::SetMetaData(FILE * /* _in */, CAudioMetaData* m) +bool CFfmpegDec::SetMetaData(FILE *_in, CFile::FileType ft, CAudioMetaData* m) { - bool needsInit = (avc == NULL); if (!meta_data_valid) { - if (needsInit && !Init()) - return false; - if (0 > avformat_find_stream_info(avc, NULL)) + if (!Init(_in, ft)) return false; - av_dump_format(avc, 0, "", 0); + if (!is_stream) { + AVDictionaryEntry *tag = NULL; + while ((tag = av_dict_get(avc->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { + if(!strcasecmp(tag->key,"Title")) { + title = tag->value; + continue; + } + if(!strcasecmp(tag->key,"Artist")) { + artist = tag->value; + continue; + } + if(!strcasecmp(tag->key,"Year")) { + date = tag->value; + continue; + } + if(!strcasecmp(tag->key,"Album")) { + album = tag->value; + continue; + } + if(!strcasecmp(tag->key,"Genre")) { + genre = tag->value; + continue; + } + } + } + + mutex.lock(); + if (avformat_find_stream_info(avc, NULL)) { + mutex.unlock(); + DeInit(); + return false; + } + mutex.unlock(); + +// fseek((FILE *) in, 0, SEEK_SET); +// av_dump_format(avc, 0, "", 0); codec = NULL; best_stream = av_find_best_stream(avc, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0); + if (best_stream < 0) { - if (needsInit) - DeInit(); + DeInit(); return false; } if (!codec) codec = avcodec_find_decoder(avc->streams[best_stream]->codec->codec_id); - mSampleRate = avc->streams[best_stream]->codec->sample_rate; + samplerate = avc->streams[best_stream]->codec->sample_rate; mChannels = av_get_channel_layout_nb_channels(avc->streams[best_stream]->codec->channel_layout); + std::stringstream ss; + ss.str("unknown"); + if (codec) + ss << std::string(codec->long_name) + " / " << mChannels << " channel" << ( mChannels > 1 ? "s" : ""); + type_info = ss.str(); + + bitrate = 0; + total_time = 0; + for(unsigned int i = 0; i < avc->nb_streams; i++) + if (avc->streams[i]->codec->bit_rate > 0) + bitrate += avc->streams[i]->codec->bit_rate; + if(m->filesize && bitrate) + total_time = 8 * m->filesize / bitrate; + + meta_data_valid = true; } - m->samplerate = mSampleRate; - std::stringstream ss; - if (codec) - ss << std::string(codec->long_name) + " / " << mChannels << " channel" << ( mChannels > 1 ? "s" : ""); - m->type_info = ss.str(); - m->changed=true; - if (needsInit) - DeInit(); + m->title = title; + m->artist = artist; + m->date = date; + m->album = album; + m->genre = genre; + m->type_info = type_info; + m->total_time = total_time; + m->bitrate = bitrate; + m->samplerate = samplerate; + return true; } diff --git a/src/driver/audiodec/ffmpegdec.h b/src/driver/audiodec/ffmpegdec.h index 223a52092..0e1920562 100644 --- a/src/driver/audiodec/ffmpegdec.h +++ b/src/driver/audiodec/ffmpegdec.h @@ -40,11 +40,15 @@ extern "C" { #include } +#include +#include class CFfmpegDec : public CBaseDec { private: bool meta_data_valid; + bool is_stream; + int mChannels; int mSampleRate; size_t buffer_size; @@ -53,19 +57,29 @@ private: AVCodec *codec; int best_stream; void *in; - bool Init(void); + bool Init(void *_in, const CFile::FileType ft); void DeInit(void); + std::string title; + std::string artist; + std::string date; + std::string album; + std::string genre; + std::string type_info; + time_t total_time; + int bitrate; + int samplerate; + public: static CFfmpegDec* getInstance(); - virtual RetCode Decoder(FILE *, int, State*, CAudioMetaData* m, time_t* t, unsigned int* secondsToSkip); - bool GetMetaData(FILE *in, const bool nice, CAudioMetaData* m); + virtual RetCode Decoder(FILE *, const CFile::FileType ft, int, State*, CAudioMetaData* m, time_t* t, unsigned int* secondsToSkip); + bool GetMetaData(FILE *in, const CFile::FileType ft, const bool nice, CAudioMetaData* m); CFfmpegDec(); ~CFfmpegDec(); int Read(void *buf, size_t buf_size); int64_t Seek(int64_t offset, int whence); protected: - virtual bool SetMetaData(FILE* in, CAudioMetaData* m); + virtual bool SetMetaData(FILE* in, const CFile::FileType ft, CAudioMetaData* m); }; #endif