diff --git a/xbmc/cores/dvdplayer/Codecs/DllAvCodec.h b/xbmc/cores/dvdplayer/Codecs/DllAvCodec.h index 87b9f95..be75c52 100644 --- a/xbmc/cores/dvdplayer/Codecs/DllAvCodec.h +++ b/xbmc/cores/dvdplayer/Codecs/DllAvCodec.h @@ -23,12 +23,14 @@ extern "C" { #if (defined USE_EXTERNAL_FFMPEG) #if (defined HAVE_LIBAVCODEC_AVCODEC_H) #include + #include #include #if (defined AVPACKET_IN_AVFORMAT) #include #endif #elif (defined HAVE_FFMPEG_AVCODEC_H) #include + #include #include #if (defined AVPACKET_IN_AVFORMAT) #include @@ -39,6 +41,7 @@ extern "C" { #include "xbmc/cores/dvdplayer/Codecs/ffmpeg/libavutil/crc.h" #else #include "libavcodec/avcodec.h" + #include "libavcodec/ac3.h" #include "libavcodec/audioconvert.h" #include "libavutil/crc.h" #endif diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.cpp b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.cpp index 96958da..f623ed0 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.cpp +++ b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.cpp @@ -26,6 +26,10 @@ #include "Settings.h" #include "utils/log.h" +#include "../../DVDStreamInfo.h" +#include "../DVDFactoryCodec.h" +#include "Encoders/DVDAudioEncoderAC3.h" + //These values are forced to allow spdif out #define OUT_SAMPLESIZE 16 #define OUT_CHANNELS 2 @@ -40,6 +44,8 @@ CDVDAudioCodecPassthroughFFmpeg::CDVDAudioCodecPassthroughFFmpeg(void) m_pFormat = NULL; m_pStream = NULL; m_pSyncFrame = NULL; + m_codec = NULL; + m_encoder = NULL; m_OutputSize = 0; m_OutputBuffer = (BYTE*)_aligned_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE + FF_INPUT_BUFFER_PADDING_SIZE, 16); @@ -52,34 +58,55 @@ CDVDAudioCodecPassthroughFFmpeg::~CDVDAudioCodecPassthroughFFmpeg(void) Dispose(); } -bool CDVDAudioCodecPassthroughFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options) +bool CDVDAudioCodecPassthroughFFmpeg::SetupEncoder(CDVDStreamInfo &hints) { - bool bSupportsAC3Out = false; - bool bSupportsDTSOut = false; - bool bSupportsAACOut = false; - bool bSupportsMP1Out = false; - bool bSupportsMP2Out = false; - bool bSupportsMP3Out = false; + /* there is no point encoding < 2 channel sources, and we dont support anything but AC3 at the moment */ + if (hints.channels <= 2 || !m_bSupportsAC3Out) + return false; + + /* we need to decode the incoming audio data, so we can re-encode it as we need it */ + m_codec = CDVDFactoryCodec::CreateAudioCodec(hints, false); + if (!m_codec) + return false; + + /* try to open the encoder */ + m_encoder = new CDVDAudioEncoderAC3(); + if (!m_encoder->Initialize(m_codec->GetChannels(), m_codec->GetChannelMap(), m_codec->GetBitsPerSample(), m_codec->GetSampleRate())) + { + Dispose(); + return false; + } + + /* adjust the hints according to the encorders output */ + hints.codec = m_encoder->GetCodecID (); + hints.bitrate = m_encoder->GetBitRate (); + hints.channels = OUT_CHANNELS; + return true; +} +bool CDVDAudioCodecPassthroughFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptions &options) +{ // TODO - move this stuff somewhere else if (g_guiSettings.GetInt("audiooutput.mode") == AUDIO_DIGITAL) { - bSupportsAC3Out = g_guiSettings.GetBool("audiooutput.ac3passthrough"); - bSupportsDTSOut = g_guiSettings.GetBool("audiooutput.dtspassthrough"); - bSupportsAACOut = g_guiSettings.GetBool("audiooutput.aacpassthrough"); - bSupportsMP1Out = g_guiSettings.GetBool("audiooutput.mp1passthrough"); - bSupportsMP2Out = g_guiSettings.GetBool("audiooutput.mp2passthrough"); - bSupportsMP3Out = g_guiSettings.GetBool("audiooutput.mp3passthrough"); + m_bSupportsAC3Out = g_guiSettings.GetBool("audiooutput.ac3passthrough"); + m_bSupportsDTSOut = g_guiSettings.GetBool("audiooutput.dtspassthrough"); + m_bSupportsAACOut = g_guiSettings.GetBool("audiooutput.aacpassthrough"); + m_bSupportsMP1Out = g_guiSettings.GetBool("audiooutput.mp1passthrough"); + m_bSupportsMP2Out = g_guiSettings.GetBool("audiooutput.mp2passthrough"); + m_bSupportsMP3Out = g_guiSettings.GetBool("audiooutput.mp3passthrough"); } + else + return false; //Samplerate cannot be checked here as we don't know it at this point in time. //We should probably have a way to try to decode data so that we know what samplerate it is. - if ((hints.codec == CODEC_ID_AC3 && bSupportsAC3Out) - || (hints.codec == CODEC_ID_DTS && bSupportsDTSOut) - || (hints.codec == CODEC_ID_AAC && bSupportsAACOut) - || (hints.codec == CODEC_ID_MP1 && bSupportsMP1Out) - || (hints.codec == CODEC_ID_MP2 && bSupportsMP2Out) - || (hints.codec == CODEC_ID_MP3 && bSupportsMP3Out)) + if ((hints.codec == CODEC_ID_AC3 && m_bSupportsAC3Out) + || (hints.codec == CODEC_ID_DTS && m_bSupportsDTSOut) + || (hints.codec == CODEC_ID_AAC && m_bSupportsAACOut) + || (hints.codec == CODEC_ID_MP1 && m_bSupportsMP1Out) + || (hints.codec == CODEC_ID_MP2 && m_bSupportsMP2Out) + || (hints.codec == CODEC_ID_MP3 && m_bSupportsMP3Out)) { // TODO - this is only valid for video files, and should be moved somewhere else @@ -108,19 +135,28 @@ bool CDVDAudioCodecPassthroughFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptio /* see if the muxer supports our codec (see spdif.c for supported formats) */ m_pSyncFrame = NULL; - switch(hints.codec) { - case CODEC_ID_AC3: m_pSyncFrame = &CDVDAudioCodecPassthroughFFmpeg::SyncAC3; break; - case CODEC_ID_DTS: m_pSyncFrame = &CDVDAudioCodecPassthroughFFmpeg::SyncDTS; break; - case CODEC_ID_MP1: - case CODEC_ID_MP2: - case CODEC_ID_MP3: - case CODEC_ID_AAC: - break; - - default: - CLog::Log(LOGERROR, "CDVDAudioCodecPassthroughFFmpeg::Open - ffmpeg SPDIF muxer does not support this codec"); - Dispose(); - return false; + bool setupEncoder = true; + while(true) { + switch(hints.codec) { + case CODEC_ID_AC3: m_pSyncFrame = &CDVDAudioCodecPassthroughFFmpeg::SyncAC3; break; + case CODEC_ID_DTS: m_pSyncFrame = &CDVDAudioCodecPassthroughFFmpeg::SyncDTS; break; + case CODEC_ID_MP1: + case CODEC_ID_MP2: + case CODEC_ID_MP3: + case CODEC_ID_AAC: + break; + + default: + if (setupEncoder && SetupEncoder(hints)) + { + setupEncoder = false; + continue; + } + CLog::Log(LOGERROR, "CDVDAudioCodecPassthroughFFmpeg::Open - FFmpeg SPDIF muxer does not support this codec"); + Dispose(); + return false; + } + break; } /* allocate a the format context */ @@ -186,7 +222,20 @@ bool CDVDAudioCodecPassthroughFFmpeg::Open(CDVDStreamInfo &hints, CDVDCodecOptio void CDVDAudioCodecPassthroughFFmpeg::Dispose() { - if (m_pFormat) { + if (m_encoder) + { + delete m_encoder; + m_encoder = NULL; + } + + if (m_codec) + { + delete m_codec; + m_codec = NULL; + } + + if (m_pFormat) + { if (m_pFormat->pb) m_dllAvUtil.av_freep(&m_pFormat->pb); m_dllAvUtil.av_freep(&m_pFormat); @@ -280,11 +329,11 @@ int CDVDAudioCodecPassthroughFFmpeg::SyncDTS(BYTE* pData, int iSize, int *fSize) ) continue; /* if it is not a termination frame, check the next 6 bits */ - if (pData[4] & 0x80 != 0 && pData[4] & 0x7C != 0x7C) + if ((pData[4] & 0x80) != 0 && (pData[4] & 0x7C) != 0x7C) continue; /* get and validate the framesize */ - *fSize = ((pData[5] & 0x3) << 8 | pData[6]) << 4 | ((pData[7] & 0xF0) >> 4) + 1; + *fSize = ((((pData[5] & 0x3) << 8 | pData[6]) << 4) | ((pData[7] & 0xF0) >> 4)) + 1; if (*fSize < 95 || *fSize > 16383) continue; @@ -298,12 +347,32 @@ int CDVDAudioCodecPassthroughFFmpeg::SyncDTS(BYTE* pData, int iSize, int *fSize) int CDVDAudioCodecPassthroughFFmpeg::Decode(BYTE* pData, int iSize) { - int fSize = iSize; - if (m_pSyncFrame) + int used, fSize; + used = fSize = iSize; + + /* if we are encoding */ + if (m_encoder) { - int skip = (this->*m_pSyncFrame)(pData, iSize, &fSize); - if (skip > 0) - return skip; + BYTE *decData; + BYTE encData[6144]; + used = m_codec ->Decode (pData, iSize); + iSize = m_codec ->GetData(&decData); + iSize = m_encoder->Encode (decData, iSize); + fSize = m_encoder->GetData(encData, 6144); + pData = encData; + + /* if the encoder has not got anything yet, return the consumed bytes */ + if (iSize == 0) + return used; + } + else + { + if (m_pSyncFrame) + { + int skip = (this->*m_pSyncFrame)(pData, iSize, &fSize); + if (skip > 0) + return skip; + } } AVPacket pkt; @@ -314,7 +383,8 @@ int CDVDAudioCodecPassthroughFFmpeg::Decode(BYTE* pData, int iSize) m_dllAvFormat.av_write_header (m_pFormat); m_dllAvFormat.av_write_frame (m_pFormat, &pkt); m_dllAvFormat.av_write_trailer(m_pFormat); - return fSize; + + return used; } int CDVDAudioCodecPassthroughFFmpeg::GetData(BYTE** dst) diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.h b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.h index 1971c56..25d6f50 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.h +++ b/xbmc/cores/dvdplayer/DVDCodecs/Audio/DVDAudioCodecPassthroughFFmpeg.h @@ -26,6 +26,7 @@ #include "Codecs/DllAvCodec.h" #include "DVDAudioCodec.h" +#include "Encoders/IDVDAudioEncoder.h" class CDVDAudioCodecPassthroughFFmpeg : public CDVDAudioCodec { @@ -46,6 +47,17 @@ public: virtual const char* GetName() { return "PassthroughFFmpeg"; } private: + bool m_bSupportsAC3Out; + bool m_bSupportsDTSOut; + bool m_bSupportsAACOut; + bool m_bSupportsMP1Out; + bool m_bSupportsMP2Out; + bool m_bSupportsMP3Out; + + CDVDAudioCodec *m_codec ; + IDVDAudioEncoder *m_encoder; + bool SetupEncoder(CDVDStreamInfo &hints); + int (CDVDAudioCodecPassthroughFFmpeg::*m_pSyncFrame)(BYTE* pData, int iSize, int *fSize); int SyncAC3(BYTE* pData, int iSize, int *fSize); int SyncDTS(BYTE* pData, int iSize, int *fSize); diff --git a/xbmc/cores/dvdplayer/DVDCodecs/Audio/Makefile.in b/xbmc/cores/dvdplayer/DVDCodecs/Audio/Makefile.in index 303fd2c..33410b9 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/Audio/Makefile.in +++ b/xbmc/cores/dvdplayer/DVDCodecs/Audio/Makefile.in @@ -13,6 +13,7 @@ SRCS= DVDAudioCodecFFmpeg.cpp \ DVDAudioCodecPassthrough.cpp \ DVDAudioCodecPassthroughFFmpeg.cpp \ DVDAudioCodecPcm.cpp \ + Encoders/DVDAudioEncoderAC3.cpp LIB=Audio.a diff --git a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp index 2530221..f802616 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp +++ b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.cpp @@ -177,18 +177,21 @@ CDVDVideoCodec* CDVDFactoryCodec::CreateVideoCodec( CDVDStreamInfo &hint ) return NULL; } -CDVDAudioCodec* CDVDFactoryCodec::CreateAudioCodec( CDVDStreamInfo &hint ) +CDVDAudioCodec* CDVDFactoryCodec::CreateAudioCodec( CDVDStreamInfo &hint, bool passthrough /* = true */) { CDVDAudioCodec* pCodec = NULL; CDVDCodecOptions options; + if (passthrough) + { #if defined(USE_LIBA52_DECODER) || defined(USE_LIBDTS_DECODER) - pCodec = OpenCodec( new CDVDAudioCodecPassthrough(), hint, options ); - if( pCodec ) return pCodec; + pCodec = OpenCodec( new CDVDAudioCodecPassthrough(), hint, options ); + if( pCodec ) return pCodec; #endif - pCodec = OpenCodec( new CDVDAudioCodecPassthroughFFmpeg(), hint, options); - if ( pCodec ) return pCodec; + pCodec = OpenCodec( new CDVDAudioCodecPassthroughFFmpeg(), hint, options); + if ( pCodec ) return pCodec; + } switch (hint.codec) { diff --git a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.h b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.h index 09885b0..317100e 100644 --- a/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.h +++ b/xbmc/cores/dvdplayer/DVDCodecs/DVDFactoryCodec.h @@ -36,7 +36,7 @@ class CDVDFactoryCodec { public: static CDVDVideoCodec* CreateVideoCodec(CDVDStreamInfo &hint ); - static CDVDAudioCodec* CreateAudioCodec(CDVDStreamInfo &hint ); + static CDVDAudioCodec* CreateAudioCodec(CDVDStreamInfo &hint, bool passthrough = true ); static CDVDOverlayCodec* CreateOverlayCodec(CDVDStreamInfo &hint ); static CDVDAudioCodec* OpenCodec(CDVDAudioCodec* pCodec, CDVDStreamInfo &hint, CDVDCodecOptions &options );