diff --git a/xbmc/android/jni/AudioFormat.cpp b/xbmc/android/jni/AudioFormat.cpp index 9d33886928e35..e393ecb3215ad 100644 --- a/xbmc/android/jni/AudioFormat.cpp +++ b/xbmc/android/jni/AudioFormat.cpp @@ -25,6 +25,11 @@ using namespace jni; int CJNIAudioFormat::ENCODING_PCM_16BIT = 0x00000002; +int CJNIAudioFormat::ENCODING_AC3 = 0x00000005; +int CJNIAudioFormat::ENCODING_E_AC3 = 0x00000006; +int CJNIAudioFormat::ENCODING_DTS = 0x00000007; +int CJNIAudioFormat::ENCODING_DTS_HD = 0x00000008; +int CJNIAudioFormat::ENCODING_DOLBY_TRUEHD = 0x00000009; int CJNIAudioFormat::CHANNEL_OUT_STEREO = 0x0000000c; int CJNIAudioFormat::CHANNEL_OUT_5POINT1 = 0x000000fc; @@ -64,11 +69,20 @@ void CJNIAudioFormat::PopulateStaticFields() CJNIAudioFormat::CHANNEL_OUT_BACK_CENTER = get_static_field(c, "CHANNEL_OUT_BACK_CENTER"); CJNIAudioFormat::CHANNEL_OUT_BACK_RIGHT = get_static_field(c, "CHANNEL_OUT_BACK_RIGHT"); CJNIAudioFormat::CHANNEL_INVALID = get_static_field(c, "CHANNEL_INVALID"); - if (sdk >= 21) - { - CJNIAudioFormat::CHANNEL_OUT_SIDE_LEFT = get_static_field(c, "CHANNEL_OUT_SIDE_LEFT"); - CJNIAudioFormat::CHANNEL_OUT_SIDE_RIGHT = get_static_field(c, "CHANNEL_OUT_SIDE_RIGHT"); - } + } + if (sdk >= 21) + { + CJNIAudioFormat::CHANNEL_OUT_SIDE_LEFT = get_static_field(c, "CHANNEL_OUT_SIDE_LEFT"); + CJNIAudioFormat::CHANNEL_OUT_SIDE_RIGHT = get_static_field(c, "CHANNEL_OUT_SIDE_RIGHT"); + + CJNIAudioFormat::ENCODING_AC3 = get_static_field(c, "ENCODING_AC3"); + CJNIAudioFormat::ENCODING_E_AC3 = get_static_field(c, "ENCODING_E_AC3"); + } + if (sdk >= 23) + { + CJNIAudioFormat::ENCODING_DTS = get_static_field(c, "ENCODING_DTS"); + CJNIAudioFormat::ENCODING_DTS_HD = get_static_field(c, "ENCODING_DTS_HD"); + //CJNIAudioFormat::ENCODING_DOLBY_TRUEHD = get_static_field(c, "ENCODING_DOLBY_TRUEHD"); } } } diff --git a/xbmc/android/jni/AudioFormat.h b/xbmc/android/jni/AudioFormat.h index a90992ea5194b..3dc52b5a82f38 100644 --- a/xbmc/android/jni/AudioFormat.h +++ b/xbmc/android/jni/AudioFormat.h @@ -28,6 +28,11 @@ class CJNIAudioFormat static void PopulateStaticFields(); static int ENCODING_PCM_16BIT; + static int ENCODING_AC3; + static int ENCODING_E_AC3; + static int ENCODING_DTS; + static int ENCODING_DTS_HD; + static int ENCODING_DOLBY_TRUEHD; static int CHANNEL_OUT_STEREO; static int CHANNEL_OUT_5POINT1; diff --git a/xbmc/android/jni/AudioTrack.cpp b/xbmc/android/jni/AudioTrack.cpp index 7098a9888d1ed..36809dca3a5e7 100644 --- a/xbmc/android/jni/AudioTrack.cpp +++ b/xbmc/android/jni/AudioTrack.cpp @@ -25,6 +25,8 @@ using namespace jni; int CJNIAudioTrack::MODE_STREAM = 0x00000001; int CJNIAudioTrack::PLAYSTATE_PLAYING = 0x00000003; +int CJNIAudioTrack::PLAYSTATE_STOPPED = 0x00000001; +int CJNIAudioTrack::PLAYSTATE_PAUSED = 0x00000002; void CJNIAudioTrack::PopulateStaticFields() { @@ -32,6 +34,8 @@ void CJNIAudioTrack::PopulateStaticFields() { jhclass c = find_class("android/media/AudioTrack"); CJNIAudioTrack::PLAYSTATE_PLAYING = get_static_field(c, "PLAYSTATE_PLAYING"); + CJNIAudioTrack::PLAYSTATE_STOPPED = get_static_field(c, "PLAYSTATE_STOPPED"); + CJNIAudioTrack::PLAYSTATE_PAUSED = get_static_field(c, "PLAYSTATE_PAUSED"); if (CJNIBase::GetSDKVersion() >= 5) CJNIAudioTrack::MODE_STREAM = get_static_field(c, "MODE_STREAM"); } diff --git a/xbmc/android/jni/AudioTrack.h b/xbmc/android/jni/AudioTrack.h index 23a587b88ee7e..8724e5d33b890 100644 --- a/xbmc/android/jni/AudioTrack.h +++ b/xbmc/android/jni/AudioTrack.h @@ -44,7 +44,9 @@ class CJNIAudioTrack : public CJNIBase static int MODE_STREAM; static int PLAYSTATE_PLAYING; - + static int PLAYSTATE_STOPPED; + static int PLAYSTATE_PAUSED; + static void PopulateStaticFields(); static int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat); static int getNativeOutputSampleRate(int streamType); diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp index f9461ef954f2c..b8b5742ea1bb4 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp @@ -21,12 +21,14 @@ #include "AESinkAUDIOTRACK.h" #include "cores/AudioEngine/Utils/AEUtil.h" #include "cores/AudioEngine/Utils/AERingBuffer.h" +#include "cores/AudioEngine/Utils/AEPackIEC61937.h" #include "android/activity/XBMCApp.h" #include "settings/Settings.h" #if defined(HAS_LIBAMCODEC) #include "utils/AMLUtils.h" #endif #include "utils/log.h" +#include "utils/StringUtils.h" #include "android/jni/AudioFormat.h" #include "android/jni/AudioManager.h" @@ -161,16 +163,16 @@ static int AEChannelMapToAUDIOTRACKChannelMask(CAEChannelInfo info) return atMask; } -static jni::CJNIAudioTrack *CreateAudioTrack(int sampleRate, int channelMask, int bufferSize) +static jni::CJNIAudioTrack *CreateAudioTrack(int stream, int sampleRate, int channelMask, int encoding, int bufferSize) { jni::CJNIAudioTrack *jniAt = NULL; try { - jniAt = new CJNIAudioTrack(CJNIAudioManager::STREAM_MUSIC, + jniAt = new CJNIAudioTrack(stream, sampleRate, channelMask, - CJNIAudioFormat::ENCODING_PCM_16BIT, + encoding, bufferSize, CJNIAudioTrack::MODE_STREAM); } @@ -188,11 +190,12 @@ CAEDeviceInfo CAESinkAUDIOTRACK::m_info; CAESinkAUDIOTRACK::CAESinkAUDIOTRACK() { m_alignedS16 = NULL; - m_min_frames = 0; m_sink_frameSize = 0; m_audiotrackbuffer_sec = 0.0; m_at_jni = NULL; m_frames_written = 0; + m_lastHeadPosition = 0; + m_ptOffset = 0; } CAESinkAUDIOTRACK::~CAESinkAUDIOTRACK() @@ -210,39 +213,102 @@ bool CAESinkAUDIOTRACK::Initialize(AEAudioFormat &format, std::string &device) { m_format = format; m_volume = -1; + m_ptBuffer = NULL; + m_silenceframes = 0; + + int stream = CJNIAudioManager::STREAM_MUSIC; + m_encoding = CJNIAudioFormat::ENCODING_PCM_16BIT; + + m_sink_sampleRate = CJNIAudioTrack::getNativeOutputSampleRate(CJNIAudioManager::STREAM_MUSIC); + for (size_t i = 0; i < m_info.m_sampleRates.size(); i++) + { + if (m_format.m_sampleRate == m_info.m_sampleRates[i]) + { + m_sink_sampleRate = m_format.m_sampleRate; + break; + } + } + m_format.m_sampleRate = m_sink_sampleRate; if (AE_IS_RAW(m_format.m_dataFormat)) + { m_passthrough = true; + if (!WantsIEC61937()) + { + switch (m_format.m_dataFormat) + { + case AE_FMT_AC3: + m_encoding = CJNIAudioFormat::ENCODING_AC3; + m_format.m_frames = AC3_FRAME_SIZE; + break; + + case AE_FMT_EAC3: + m_encoding = CJNIAudioFormat::ENCODING_E_AC3; + m_sink_sampleRate = m_format.m_sampleRate / 4; + m_format.m_frames = EAC3_FRAME_SIZE; + break; + + case AE_FMT_DTS: + m_encoding = CJNIAudioFormat::ENCODING_DTS; + m_format.m_frames = DTS1_FRAME_SIZE; + break; + + case AE_FMT_DTSHD: + m_encoding = CJNIAudioFormat::ENCODING_DTS_HD; + break; + + case AE_FMT_TRUEHD: + m_encoding = CJNIAudioFormat::ENCODING_DOLBY_TRUEHD; + break; + + default: + break; + } + } + else + m_format.m_dataFormat = AE_FMT_S16LE; + } else + { m_passthrough = false; + m_format.m_dataFormat = AE_FMT_S16LE; + } + + int atChannelMask = AEChannelMapToAUDIOTRACKChannelMask(m_format.m_channelLayout); + m_format.m_channelLayout = AUDIOTRACKChannelMaskToAEChannelMap(atChannelMask); + m_format.m_frameSize = m_format.m_channelLayout.Count() * + (CAEUtil::DataFormatToBits(m_format.m_dataFormat) / 8); + m_sink_frameSize = m_format.m_frameSize; #if defined(HAS_LIBAMCODEC) if (CSettings::GetInstance().GetBool(CSettings::SETTING_VIDEOPLAYER_USEAMCODEC)) aml_set_audio_passthrough(m_passthrough); #endif - int atChannelMask = AEChannelMapToAUDIOTRACKChannelMask(m_format.m_channelLayout); - - m_format.m_sampleRate = CJNIAudioTrack::getNativeOutputSampleRate(CJNIAudioManager::STREAM_MUSIC); - m_format.m_dataFormat = AE_FMT_S16LE; - while (!m_at_jni) { - m_format.m_channelLayout = AUDIOTRACKChannelMaskToAEChannelMap(atChannelMask); - m_format.m_frameSize = m_format.m_channelLayout.Count() * - (CAEUtil::DataFormatToBits(m_format.m_dataFormat) / 8); - int min_buffer_size = CJNIAudioTrack::getMinBufferSize( m_format.m_sampleRate, + unsigned int min_buffer_size = CJNIAudioTrack::getMinBufferSize( m_sink_sampleRate, atChannelMask, - CJNIAudioFormat::ENCODING_PCM_16BIT); - m_sink_frameSize = m_format.m_channelLayout.Count() * - (CAEUtil::DataFormatToBits(AE_FMT_S16LE) / 8); - m_min_frames = min_buffer_size / m_sink_frameSize; - m_audiotrackbuffer_sec = (double)m_min_frames / (double)m_format.m_sampleRate; - - m_at_jni = CreateAudioTrack(m_format.m_sampleRate, - atChannelMask, + m_encoding); + if (m_passthrough && !WantsIEC61937()) + { + min_buffer_size = (min_buffer_size * 4 / (m_format.m_frameSize*m_format.m_frames)+1) * (m_format.m_frameSize*m_format.m_frames); + m_ptBuffer = (uint8_t*)malloc(m_sink_frameSize * m_format.m_frames); + } + else + { + m_format.m_frames = (int)(min_buffer_size / m_sink_frameSize) / 2; + } + + m_format.m_frameSamples = m_format.m_frames * m_format.m_channelLayout.Count(); + m_audiotrackbuffer_sec = (double)(min_buffer_size / m_sink_frameSize) / (double)m_format.m_sampleRate; + + m_at_jni = CreateAudioTrack(stream, m_sink_sampleRate, + atChannelMask, m_encoding, min_buffer_size); + CLog::Log(LOGDEBUG, "CAESinkAUDIOTRACK::Initialize m_sampleRate %u; min_buffer_size %u; m_frames %u; m_frameSize %u", m_format.m_sampleRate, min_buffer_size, m_format.m_frames, m_format.m_frameSize); + if (!m_at_jni) { if (atChannelMask != CJNIAudioFormat::CHANNEL_OUT_STEREO && @@ -264,9 +330,6 @@ bool CAESinkAUDIOTRACK::Initialize(AEAudioFormat &format, std::string &device) } } - m_format.m_frames = m_min_frames / 2; - - m_format.m_frameSamples = m_format.m_frames * m_format.m_channelLayout.Count(); format = m_format; // Force volume to 100% for passthrough @@ -297,6 +360,10 @@ void CAESinkAUDIOTRACK::Deinitialize() m_at_jni->release(); m_frames_written = 0; + m_lastHeadPosition = 0; + m_ptOffset = 0; + if (m_ptBuffer) + SAFE_DELETE(m_ptBuffer); delete m_at_jni; m_at_jni = NULL; @@ -315,7 +382,20 @@ void CAESinkAUDIOTRACK::GetDelay(AEDelayStatus& status) // for wrap saftey, we need to do all ops on it in 32bit integer math. uint32_t head_pos = (uint32_t)m_at_jni->getPlaybackHeadPosition(); - double delay = (double)(m_frames_written - head_pos) / m_format.m_sampleRate; + double delay; + if (m_passthrough && !WantsIEC61937()) + { + if (!head_pos && m_at_jni->getPlayState() == CJNIAudioTrack::PLAYSTATE_PAUSED) + m_ptOffset = m_lastHeadPosition; + + head_pos += m_ptOffset; + m_lastHeadPosition = head_pos; + + delay = ((double)(m_frames_written - m_silenceframes) / m_format.m_sampleRate) - ((double)head_pos / m_sink_sampleRate); + CLog::Log(LOGDEBUG, "CAESinkAUDIOTRACK::GetDelay m_frames_written/head_pos %u/%u %f", m_frames_written, head_pos, delay); + } + else + delay = (double)(m_frames_written - head_pos) / m_sink_sampleRate; status.SetDelay(delay); } @@ -339,6 +419,51 @@ unsigned int CAESinkAUDIOTRACK::AddPackets(uint8_t **data, unsigned int frames, return INT_MAX; uint8_t *buffer = data[0]+offset*m_format.m_frameSize; + uint8_t *out_buf = buffer; + int size = frames * m_format.m_frameSize; + + if (m_passthrough && !WantsIEC61937()) + { +#if 0 + CLog::Log(LOGDEBUG, "CAESinkAUDIOTRACK::AddPackets size: %d", size); + if (size) + { + std::string line; + for (unsigned int y=0; y*8 < size && y*8 < 16; ++y) + { + line = ""; + for (unsigned int x=0; x<8 && y*8 + x < size && y*8 + x < 16; ++x) + { + line += StringUtils::Format("%02x ", ((char *)buffer)[y*8+x]); + } + CLog::Log(LOGDEBUG, "%s", line.c_str()); + } + } +#endif + + CAEPackIEC61937::IEC61937Packet *packet; + packet = (CAEPackIEC61937::IEC61937Packet*)buffer; + if (packet->m_preamble1 == IEC61937_PREAMBLE1 && packet->m_preamble2 == IEC61937_PREAMBLE2) + { + int psize = packet->m_length; + if ((packet->m_type & 0x3f) < 0x10) + psize = psize >> 3; + buffer = packet->m_data; +#ifndef __BIG_ENDIAN__ + out_buf = m_ptBuffer; + CAEPackIEC61937::SwapEndian((uint16_t*)out_buf, (uint16_t*)buffer, psize >> 1); + memset(out_buf + psize, 0, size - psize); +#else + out_buf = buffer; +#endif + CLog::Log(LOGERROR, "CAESinkAUDIOTRACK::AddPackets AC3: found packet %d/%d", psize, size); + } + else + { + CLog::Log(LOGERROR, "CAESinkAUDIOTRACK::AddPackets AC3: not a valid IEC61937 packet %d", size); + m_silenceframes += frames; + } + } // write as many frames of audio as we can fit into our internal buffer. int written = 0; @@ -349,12 +474,13 @@ unsigned int CAESinkAUDIOTRACK::AddPackets(uint8_t **data, unsigned int frames, // writing into its buffer. if (m_at_jni->getPlayState() != CJNIAudioTrack::PLAYSTATE_PLAYING) m_at_jni->play(); - - written = m_at_jni->write((char*)buffer, 0, frames * m_sink_frameSize); + written = m_at_jni->write((char*)out_buf, 0, size); m_frames_written += written / m_sink_frameSize; } - return (unsigned int)(written/m_sink_frameSize); + //CLog::Log(LOGDEBUG, "CAESinkAUDIOTRACK::AddPackets written %d", written); + + return (unsigned int)(written/m_format.m_frameSize); } void CAESinkAUDIOTRACK::Drain() @@ -368,6 +494,14 @@ void CAESinkAUDIOTRACK::Drain() m_frames_written = 0; } +bool CAESinkAUDIOTRACK::WantsIEC61937() +{ + if (CJNIAudioManager::GetSDKVersion() >= 21) + return false; + + return true; +} + void CAESinkAUDIOTRACK::EnumerateDevicesEx(AEDeviceInfoList &list, bool force) { m_info.m_channels.Reset(); @@ -392,7 +526,7 @@ void CAESinkAUDIOTRACK::EnumerateDevicesEx(AEDeviceInfoList &list, bool force) if (!CXBMCApp::IsHeadsetPlugged()) { m_info.m_deviceType = AE_DEVTYPE_HDMI; - int test_sample[] = { 44100, 48000, 96000, 192000 }; + int test_sample[] = { 32000, 44100, 48000, 96000, 192000 }; int test_sample_sz = sizeof(test_sample) / sizeof(int); for (int i=0; i= 21) + { + m_info.m_sampleRates.push_back(192000); // force support + m_info.m_dataFormats.push_back(AE_FMT_EAC3); + m_info.m_dataFormats.push_back(AE_FMT_DTSHD); + m_info.m_dataFormats.push_back(AE_FMT_TRUEHD); + } } #if 0 //defined(__ARM_NEON__) if (g_cpuInfo.GetCPUFeatures() & CPU_FEATURE_NEON) diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h index d1e188657e824..367dc06fe1c6f 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h +++ b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.h @@ -45,6 +45,7 @@ class CAESinkAUDIOTRACK : public IAESink virtual double GetCacheTotal (); virtual unsigned int AddPackets (uint8_t **data, unsigned int frames, unsigned int offset); virtual void Drain (); + virtual bool WantsIEC61937 (); static void EnumerateDevicesEx(AEDeviceInfoList &list, bool force = false); protected: @@ -54,13 +55,19 @@ class CAESinkAUDIOTRACK : public IAESink jni::CJNIAudioTrack *m_at_jni; // m_frames_written must wrap at UINT32_MAX uint32_t m_frames_written; + uint32_t m_lastHeadPosition; + uint32_t m_ptOffset; static CAEDeviceInfo m_info; AEAudioFormat m_format; double m_volume; - volatile int m_min_frames; int16_t *m_alignedS16; unsigned int m_sink_frameSize; + unsigned int m_sink_sampleRate; bool m_passthrough; double m_audiotrackbuffer_sec; + int m_encoding; + + uint8_t* m_ptBuffer; + unsigned int m_silenceframes; }; diff --git a/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.cpp b/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.cpp index 3fad1b4b403ab..81dff42e1865a 100644 --- a/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.cpp +++ b/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.cpp @@ -22,15 +22,6 @@ #include "system.h" #include "AEPackIEC61937.h" -#define IEC61937_PREAMBLE1 0xF872 -#define IEC61937_PREAMBLE2 0x4E1F - -inline void SwapEndian(uint16_t *dst, uint16_t *src, unsigned int size) -{ - for (unsigned int i = 0; i < size; ++i, ++dst, ++src) - *dst = ((*src & 0xFF00) >> 8) | ((*src & 0x00FF) << 8); -} - int CAEPackIEC61937::PackAC3(uint8_t *data, unsigned int size, uint8_t *dest) { assert(size <= OUT_FRAMESTOBYTES(AC3_FRAME_SIZE)); diff --git a/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.h b/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.h index 668be1e40516a..3319c53d859f0 100644 --- a/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.h +++ b/xbmc/cores/AudioEngine/Utils/AEPackIEC61937.h @@ -32,6 +32,9 @@ #define EAC3_FRAME_SIZE 6144 #define TRUEHD_FRAME_SIZE 15360 +#define IEC61937_PREAMBLE1 0xF872 +#define IEC61937_PREAMBLE2 0x4E1F + #define OUT_SAMPLESIZE 16 #define OUT_CHANNELS 2 #define OUT_FRAMESTOBYTES(a) ((a) * OUT_CHANNELS * (OUT_SAMPLESIZE>>3)) @@ -48,7 +51,13 @@ class CAEPackIEC61937 static int PackDTS_2048(uint8_t *data, unsigned int size, uint8_t *dest, bool littleEndian); static int PackTrueHD (uint8_t *data, unsigned int size, uint8_t *dest); static int PackDTSHD (uint8_t *data, unsigned int size, uint8_t *dest, unsigned int period); -private: + + static inline void SwapEndian(uint16_t *dst, uint16_t *src, unsigned int size) + { + for (unsigned int i = 0; i < size; ++i, ++dst, ++src) + *dst = ((*src & 0xFF00) >> 8) | ((*src & 0x00FF) << 8); + } + static int PackDTS(uint8_t *data, unsigned int size, uint8_t *dest, bool littleEndian, unsigned int frameSize, uint16_t type); diff --git a/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp b/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp index 39074ffd1534d..c911010387cbe 100644 --- a/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp +++ b/xbmc/cores/dvdplayer/DVDPlayerAudio.cpp @@ -570,7 +570,7 @@ void CDVDPlayerAudio::Process() } // Zero out the frame data if we are supposed to silence the audio - if (m_silence || m_syncclock) + if (m_silence) { int size = audioframe.nb_frames * audioframe.framesize / audioframe.planes; for (unsigned int i=0; i