[depends] - bump libsdl-native and libsdl (target) to 1.2.15 and add the...
<?xml version="1.0" encoding="UTF-8"?>
<window type="dialog">
+ <zorder>1</zorder>
<defaultcontrol always="true">3</defaultcontrol>
<coordinates>
<system>1</system>
<itemgap>5</itemgap>
<orientation>horizontal</orientation>
<control type="label" id="1">
- <width min="10" max="710">auto</width>
+ <width min="10" max="520">auto</width>
<height>30</height>
<font>font30</font>
<align>left</align>
<aspectratio align="center">keep</aspectratio>
<texture>$INFO[VideoPlayer.AudioChannels,flagging/audio/,.png]</texture>
</control>
+ <control type="group" id="1">
+ <width>85</width>
+ <height>35</height>
+ <visible>!IsEmpty(VideoPlayer.AudioLanguage)</visible>
+ <control type="image" id="1">
+ <posx>5</posx>
+ <posy>0</posy>
+ <description>Audio Language Image</description>
+ <width>80</width>
+ <height>35</height>
+ <texture>flagging/AudioLang.png</texture>
+ </control>
+ <control type="label" id="1">
+ <posx>38</posx>
+ <posy>2</posy>
+ <width>47</width>
+ <height>35</height>
+ <font>font13</font>
+ <align>left</align>
+ <label>$INFO[VideoPlayer.AudioLanguage]</label>
+ <textcolor>grey</textcolor>
+ </control>
+ </control>
+ <control type="group" id="1">
+ <width>85</width>
+ <height>35</height>
+ <visible>!IsEmpty(VideoPlayer.SubtitlesLanguage)</visible>
+ <control type="image" id="1">
+ <posx>5</posx>
+ <posy>0</posy>
+ <width>80</width>
+ <height>35</height>
+ <texture>flagging/SubLang.png</texture>
+ </control>
+ <control type="label" id="1">
+ <posx>38</posx>
+ <posy>2</posy>
+ <width>47</width>
+ <height>35</height>
+ <font>font13</font>
+ <align>left</align>
+ <label>$INFO[VideoPlayer.SubtitlesLanguage]</label>
+ <textcolor>grey</textcolor>
+ </control>
+ </control>
</control>
<control type="label" id="1">
<posx>0</posx>
<width>550</width>
<height>30</height>
<font>font30_title</font>
- <label>$ADDON[script.cu.lrclyrics 0]</label>
+ <label>$ADDON[script.cu.lrclyrics 32199]</label>
<align>right</align>
<aligny>center</aligny>
<textcolor>white</textcolor>
<?xml version="1.0" encoding="UTF-8"?>
-<addon id="xbmc.json" version="6.6.0" provider-name="Team XBMC">
+<addon id="xbmc.json" version="6.7.0" provider-name="Team XBMC">
<backwards-compatibility abi="6.0.0"/>
<requires>
<import addon="xbmc.core" version="0.1.0"/>
CALL %%S
)
+SET FORMED_OK_FLAG=%TMP_PATH%\got-all-formed-packages
REM Trick to preserve console title
start /b /wait cmd.exe /c get_formed.cmd
-IF ERRORLEVEL 1 EXIT /B %ERRORLEVEL%
+IF NOT EXIST %FORMED_OK_FLAG% (
+ ECHO ERROR: Not all formed packages are ready!
+ EXIT /B 101
+)
cd %CUR_PATH%
rmdir %TMP_PATH% /S /Q
+EXIT /B 0
EXIT /B 20
)
+REM Clear succeed flag
+IF EXIST %FORMED_OK_FLAG% (
+ del /F /Q "%FORMED_OK_FLAG%" || EXIT /B 4
+)
+
CALL :setStageName Starting downloads of formed packages...
SET SCRIPT_PATH=%CD%
CD %DL_PATH% || EXIT /B 10
)
CALL :setStageName All formed packages ready.
+ECHO %DATE% %TIME% > "%FORMED_OK_FLAG%"
EXIT /B 0
REM End of main body
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Button Mappings : -->
+<!-- -->
+<!-- ID Button -->
+<!-- -->
+<!-- 1 A -->
+<!-- 2 B -->
+<!-- 3 X -->
+<!-- 4 Y -->
+<!-- 5 Left Shoulder -->
+<!-- 6 Right Shoulder -->
+<!-- 7 Back -->
+<!-- 8 Start -->
+<!-- 9 Left Stick Button -->
+<!-- 10 Right Stick Button -->
+<!-- 11 D-Pad Up -->
+<!-- 12 D-Pad Down -->
+<!-- 13 D-Pad Left -->
+<!-- 14 D-Pad Right -->
+<!-- 15 Back -->
+
+<!-- Axis Mappings: -->
+<!-- -->
+<!-- ID Button -->
+<!-- -->
+<!-- 1 Left Stick L/R -->
+<!-- 2 Left Stick U/D -->
+<!-- 3 limit +1 Left Trigger -->
+<!-- 3 limit -1 Right Trigger -->
+<!-- 4 Right Stick L/R -->
+<!-- 5 Right Stick U/D -->
+
+<keymap>
+ <global>
+ <joystick>
+ <!-- A selects. B goes back. X gets context menu. Y goes fullscreen and back. -->
+ <button id="1">Select</button>
+ <button id="2">Back</button>
+ <button id="3">ContextMenu</button>
+ <button id="4">FullScreen</button>
+ <!--Left Shoulder Queues videos. Right shoulder displays the current queue. -->
+ <button id="5">Queue</button>
+ <button id="6">Playlist</button>
+ <button id="7">PreviousMenu</button>
+ <button id="8">XBMC.ActivateWindow(Home)</button>
+ <!-- Left stick click activates the shutdown menu. -->
+ <button id="9">XBMC.ActivateWindow(ShutdownMenu)</button>
+ <button id="10">XBMC.ActivateWindow(PlayerControls)</button>
+ <button id="11">Up</button>
+ <button id="12">Down</button>
+ <button id="13">Left</button>
+ <button id="14">Right</button>
+ <button id="15">PreviousMenu</button>
+ <axis id="1" limit="-1">Up</axis>
+ <axis id="1" limit="+1">Down</axis>
+ <axis id="2" limit="-1">Left</axis>
+ <axis id="2" limit="+1">Right</axis>
+ <axis id="3" limit="+1">ScrollUp</axis>
+ <axis id="3" limit="-1">ScrollDown</axis>
+ <!-- Push up on the right stick for volueme up. Push down for volume down. -->
+ <axis id="5" limit="-1">VolumeUp</axis>
+ <axis id="5" limit="+1">VolumeDown</axis>
+ <axis id="4" limit="-1">VolumeDown</axis>
+ <axis id="4" limit="+1">VolumeUp</axis>
+ <hat id="1" position="up">Up</hat>
+ <hat id="1" position="down">Down</hat>
+ <hat id="1" position="left">Left</hat>
+ <hat id="1" position="right">Right</hat>
+ </joystick>
+ </global>
+ <Home>
+ <joystick>
+ <button id="8">XBMC.Skin.ToggleSetting(HomeViewToggle)</button>
+ </joystick>
+ </Home>
+ <MyFiles>
+ <joystick>
+ <button id="6">Highlight</button>
+ </joystick>
+ </MyFiles>
+ <MyMusicPlaylist>
+ <joystick>
+ <button id="5">Delete</button>
+ </joystick>
+ </MyMusicPlaylist>
+ <MyMusicFiles>
+ </MyMusicFiles>
+ <MyMusicLibrary>
+ </MyMusicLibrary>
+ <FullscreenVideo>
+ <joystick>
+ <!--
+ A pauses and starts the video.
+ B stops the video.
+ X opens the onscreen display.
+ Y switches in and out of full screen
+ -->
+ <button id="1">Pause</button>
+ <button id="2">Stop</button>
+ <button id="3">OSD</button>
+ <!--
+ Left shoulder changes aspect ratio.
+ Right shoulder changes subtitles.
+ Right stick changes Audio Language.
+ Start button displays info.
+ -->
+ <button id="5">AspectRatio</button>
+ <button id="6">ShowSubtitles</button>
+ <button id="7">SmallStepBack</button>
+ <button id="8">Info</button>
+ <button id="10">AudioNextLanguage</button>
+ <button id="11">BigStepForward</button>
+ <button id="12">BigStepBack</button>
+ <button id="13">StepBack</button>
+ <button id="14">StepForward</button>
+ <!-- D-pad does what you'd expect. Triggers fast forward and rewind. Left stick scans forward and back. -->
+ <axis id="3" limit="+1">AnalogRewind</axis>
+ <axis id="3" limit="-1">AnalogFastForward</axis>
+ <hat id="1" position="up">BigStepForward</hat>
+ <hat id="1" position="down">BigStepBack</hat>
+ <hat id="1" position="left">StepBack</hat>
+ <hat id="1" position="right">StepForward</hat>
+ </joystick>
+ </FullscreenVideo>
+ <FullscreenLiveTV>
+ <joystick>
+ <button id="11">ChannelUp</button>
+ <button id="12">ChannelDown</button>
+ <button id="13">PreviousChannelGroup</button>
+ <button id="14">NextChannelGroup</button>
+ <hat id="1" position="up">ChannelUp</hat>
+ <hat id="1" position="down">ChannelDown</hat>
+ <hat id="1" position="left">PreviousChannelGroup</hat>
+ <hat id="1" position="right">NextChannelGroup</hat>
+ </joystick>
+ </FullscreenLiveTV>
+ <FullscreenInfo>
+ <joystick>
+ <button id="2">Close</button>
+ <button id="3">OSD</button>
+ <button id="8">Close</button>
+ <axis id="3" limit="+1">AnalogRewind</axis>
+ <axis id="3" limit="-1">AnalogFastForward</axis>
+ </joystick>
+ </FullscreenInfo>
+ <PlayerControls>
+ <joystick>
+ <button id="3">Close</button>
+ <button id="9">Close</button>
+ <button id="10">Close</button>
+ </joystick>
+ </PlayerControls>
+ <Visualisation>
+ <joystick>
+ <button id="1">Pause</button>
+ <button id="2">Stop</button>
+ <button id="3">XBMC.ActivateWindow(MusicOSD)</button>
+ <button id="5">XBMC.ActivateWindow(VisualisationPresetList)</button>
+ <button id="6">Info</button>
+ <button id="10">XBMC.ActivateWindow(MusicOSD)</button>
+ <button id="11">SkipNext</button>
+ <button id="12">SkipPrevious</button>
+ <button id="13">PreviousPreset</button>
+ <button id="14">NextPreset</button>
+ <axis id="3" limit="+1">AnalogRewind</axis>
+ <axis id="3" limit="-1">AnalogFastForward</axis>
+ <hat id="1" position="up">SkipNext</hat>
+ <hat id="1" position="down">SkipPrevious</hat>
+ <hat id="1" position="left">PreviousPreset</hat>
+ <hat id="1" position="right">NextPreset</hat>
+ </joystick>
+ </Visualisation>
+ <MusicOSD>
+ <joystick>
+ <button id="3">Close</button>
+ <button id="6">Info</button>
+ </joystick>
+ </MusicOSD>
+ <VisualisationSettings>
+ <joystick>
+ <button id="2">Close</button>
+ </joystick>
+ </VisualisationSettings>
+ <VisualisationPresetList>
+ <joystick>
+ <button id="2">Close</button>
+ </joystick>
+ </VisualisationPresetList>
+ <SlideShow>
+ <joystick>
+ <button id="1">Pause</button>
+ <button id="2">Stop</button>
+ <button id="4">ZoomNormal</button>
+ <button id="5">Rotate</button>
+ <button id="6">CodecInfo</button>
+ <button id="11">ZoomIn</button>
+ <button id="12">ZoomOut</button>
+ <button id="13">PreviousPicture</button>
+ <button id="14">NextPicture</button>
+ <axis id="1">AnalogMove</axis>
+ <axis id="2">AnalogMove</axis>
+ <axis id="3" limit="+1">ZoomOut</axis>
+ <axis id="3" limit="-1">ZoomIn</axis>
+ <hat id="1" position="up">ZoomIn</hat>
+ <hat id="1" position="down">ZoomOut</hat>
+ <hat id="1" position="left">PreviousPicture</hat>
+ <hat id="1" position="right">NextPicture</hat>
+ </joystick>
+ </SlideShow>
+ <ScreenCalibration>
+ <joystick>
+ <button id="3">ResetCalibration</button>
+ <button id="5">NextResolution</button>
+ <button id="6">NextCalibration</button>
+ </joystick>
+ </ScreenCalibration>
+ <GUICalibration>
+ <joystick>
+ <button id="3">ResetCalibration</button>
+ <button id="5">NextResolution</button>
+ <button id="6">NextCalibration</button>
+ </joystick>
+ </GUICalibration>
+ <VideoOSD>
+ <joystick>
+ <button id="3">Close</button>
+ </joystick>
+ </VideoOSD>
+ <VideoMenu>
+ <joystick>
+ <button id="2">Stop</button>
+ <button id="3">OSD</button>
+ <button id="5">AspectRatio</button>
+ <button id="8">Info</button>
+ </joystick>
+ </VideoMenu>
+ <OSDVideoSettings>
+ <joystick>
+ <button id="5">AspectRatio</button>
+ <button id="3">Close</button>
+ </joystick>
+ </OSDVideoSettings>
+ <OSDAudioSettings>
+ <joystick>
+ <button id="5">AspectRatio</button>
+ <button id="3">Close</button>
+ </joystick>
+ </OSDAudioSettings>
+ <VideoBookmarks>
+ <joystick>
+ <button id="5">Delete</button>
+ </joystick>
+ </VideoBookmarks>
+ <MyVideoLibrary>
+ </MyVideoLibrary>
+ <MyVideoFiles>
+ </MyVideoFiles>
+ <MyVideoPlaylist>
+ <joystick>
+ <button id="5">Delete</button>
+ </joystick>
+ </MyVideoPlaylist>
+ <VirtualKeyboard>
+ <joystick>
+ <button id="2">BackSpace</button>
+ <button id="4">Symbols</button>
+ <button id="5">Shift</button>
+ <button id="9">Enter</button>
+ <axis id="3" limit="+1">CursorLeft</axis>
+ <axis id="3" limit="-1">CursorRight</axis>
+ </joystick>
+ </VirtualKeyboard>
+ <ContextMenu>
+ <joystick>
+ <button id="2">Close</button>
+ <button id="3">Close</button>
+ </joystick>
+ </ContextMenu>
+ <Scripts>
+ <joystick>
+ <button id="3">Info</button>
+ </joystick>
+ </Scripts>
+ <Settings>
+ <joystick>
+ <button id="2">PreviousMenu</button>
+ </joystick>
+ </Settings>
+ <AddonInformation>
+ <joystick>
+ <button id="2">Close</button>
+ </joystick>
+ </AddonInformation>
+ <AddonSettings>
+ <joystick>
+ <button id="2">Close</button>
+ </joystick>
+ </AddonSettings>
+ <TextViewer>
+ <joystick>
+ <button id="2">Close</button>
+ </joystick>
+ </TextViewer>
+ <shutdownmenu>
+ <joystick>
+ <button id="2">PreviousMenu</button>
+ <button id="9">PreviousMenu</button>
+ </joystick>
+ </shutdownmenu>
+ <submenu>
+ <joystick>
+ <button id="2">PreviousMenu</button>
+ </joystick>
+ </submenu>
+ <MusicInformation>
+ <joystick>
+ <button id="2">Close</button>
+ </joystick>
+ </MusicInformation>
+ <MovieInformation>
+ <joystick>
+ <button id="2">Close</button>
+ </joystick>
+ </MovieInformation>
+ <NumericInput>
+ <joystick>
+ <button id="2">BackSpace</button>
+ <button id="9">Enter</button>
+ </joystick>
+ </NumericInput>
+ <GamepadInput>
+ <joystick>
+ <button id="9">Stop</button>
+ </joystick>
+ </GamepadInput>
+ <LockSettings>
+ <joystick>
+ <button id="2">PreviousMenu</button>
+ <button id="9">Close</button>
+ </joystick>
+ </LockSettings>
+ <ProfileSettings>
+ <joystick>
+ <button id="2">PreviousMenu</button>
+ <button id="9">Close</button>
+ </joystick>
+ </ProfileSettings>
+</keymap>
<setting id="audiooutput.stereoupmix">
<visible>false</visible>
</setting>
- <setting id="audiooutput.channels">
- <visible>false</visible>
- </setting>
<setting id="audiooutput.streamsilence">
<level>2</level>
<default>0</default>
<dependencies>
<dependency type="enable" setting="lookandfeel.enablerssfeeds">true</dependency>
</dependencies>
- <control type="button" format="action" />
+ <control type="button" format="action" attributes="hide_value" />
</setting>
</group>
</category>
<setting id="masterlock.lockcode" type="string" label="20100" help="36396">
<level>2</level>
<default>-</default>
- <control type="button" format="action" />
+ <control type="button" format="action" attributes="hide_value"/>
</setting>
<setting id="masterlock.startuplock" type="boolean" label="20076" help="36397">
<level>2</level>
{ "videoaspect", VIDEOPLAYER_VIDEO_ASPECT },
{ "audiocodec", VIDEOPLAYER_AUDIO_CODEC },
{ "audiochannels", VIDEOPLAYER_AUDIO_CHANNELS },
+ { "audiolanguage", VIDEOPLAYER_AUDIO_LANG },
{ "hasteletext", VIDEOPLAYER_HASTELETEXT },
{ "lastplayed", VIDEOPLAYER_LASTPLAYED },
{ "playcount", VIDEOPLAYER_PLAYCOUNT },
strLabel.Format("%i", m_audioInfo.channels);
}
break;
+ case VIDEOPLAYER_AUDIO_LANG:
+ if(g_application.m_pPlayer->IsPlaying())
+ {
+ SPlayerAudioStreamInfo info;
+ g_application.m_pPlayer->GetAudioStreamInfo(CMediaSettings::Get().GetCurrentVideoSettings().m_AudioStream, info);
+ strLabel = info.language;
+ }
+ break;
case VIDEOPLAYER_STEREOSCOPIC_MODE:
if(g_application.m_pPlayer->IsPlaying())
{
#define VIDEOPLAYER_IS_STEREOSCOPIC 310
#define VIDEOPLAYER_STEREOSCOPIC_MODE 311
#define VIDEOPLAYER_SUBTITLES_LANG 312
+#define VIDEOPLAYER_AUDIO_LANG 313
#define CONTAINER_CAN_FILTER 342
#define CONTAINER_CAN_FILTERADVANCED 343
using namespace std;
-CImageLoader::CImageLoader(const CStdString &path)
+CImageLoader::CImageLoader(const CStdString &path, const bool useCache)
{
m_path = path;
m_texture = NULL;
+ m_use_cache = useCache;
}
CImageLoader::~CImageLoader()
bool CImageLoader::DoWork()
{
bool needsChecking = false;
+ CStdString loadPath;
CStdString texturePath = g_TextureManager.GetTexturePath(m_path);
- CStdString loadPath = CTextureCache::Get().CheckCachedImage(texturePath, true, needsChecking);
+ if (m_use_cache)
+ loadPath = CTextureCache::Get().CheckCachedImage(texturePath, true, needsChecking);
+ else
+ loadPath = texturePath;
- if (loadPath.IsEmpty())
+ if (m_use_cache && loadPath.IsEmpty())
{
// not in our texture cache, so try and load directly and then cache the result
loadPath = CTextureCache::Get().CacheImage(texturePath, &m_texture);
if (m_texture)
return true; // we're done
}
- if (!loadPath.IsEmpty())
+ if (!m_use_cache || !loadPath.IsEmpty())
{
// direct route - load the image
unsigned int start = XbmcThreads::SystemClockMillis();
// if available, increment reference count, and return the image.
// else, add to the queue list if appropriate.
-bool CGUILargeTextureManager::GetImage(const CStdString &path, CTextureArray &texture, bool firstRequest)
+bool CGUILargeTextureManager::GetImage(const CStdString &path, CTextureArray &texture, bool firstRequest, const bool useCache)
{
CSingleLock lock(m_listSection);
for (listIterator it = m_allocated.begin(); it != m_allocated.end(); ++it)
}
if (firstRequest)
- QueueImage(path);
+ QueueImage(path, useCache);
return true;
}
}
// queue the image, and start the background loader if necessary
-void CGUILargeTextureManager::QueueImage(const CStdString &path)
+void CGUILargeTextureManager::QueueImage(const CStdString &path, bool useCache)
{
CSingleLock lock(m_listSection);
for (queueIterator it = m_queued.begin(); it != m_queued.end(); ++it)
// queue the item
CLargeTexture *image = new CLargeTexture(path);
- unsigned int jobID = CJobManager::GetInstance().AddJob(new CImageLoader(path), this, CJob::PRIORITY_NORMAL);
+ unsigned int jobID = CJobManager::GetInstance().AddJob(new CImageLoader(path, useCache), this, CJob::PRIORITY_NORMAL);
m_queued.push_back(make_pair(jobID, image));
}
class CImageLoader : public CJob
{
public:
- CImageLoader(const CStdString &path);
+ CImageLoader(const CStdString &path, const bool useCache);
virtual ~CImageLoader();
/*!
*/
virtual bool DoWork();
+ bool m_use_cache; ///< Whether or not to use any caching with this image
CStdString m_path; ///< path of image to load
CBaseTexture *m_texture; ///< Texture object to load the image into \sa CBaseTexture.
};
\return true if the image exists, else false.
\sa CGUITextureArray and CGUITexture
*/
- bool GetImage(const CStdString &path, CTextureArray &texture, bool firstRequest);
+ bool GetImage(const CStdString &path, CTextureArray &texture, bool firstRequest, bool useCache = true);
/*!
\brief Request a texture to be unloaded.
unsigned int m_timeToDelete;
};
- void QueueImage(const CStdString &path);
+ void QueueImage(const CStdString &path, bool useCache = true);
std::vector< std::pair<unsigned int, CLargeTexture *> > m_queued;
std::vector<CLargeTexture *> m_allocated;
#include "FileItem.h"
#include "music/MusicThumbLoader.h"
#include "music/tags/MusicInfoTag.h"
+#if defined(HAS_OMXPLAYER)
+#include "cores/omxplayer/OMXImage.h"
+#endif
CTextureCacheJob::CTextureCacheJob(const CStdString &url, const CStdString &oldHash)
{
else if (m_details.hash == m_oldHash)
return true;
+#if defined(HAS_OMXPLAYER)
+ if (COMXImage::CreateThumb(image, width, height, additional_info, CTextureCache::GetCachedPath(m_cachePath + ".jpg")))
+ {
+ m_details.width = width;
+ m_details.height = height;
+ m_details.file = m_cachePath + ".jpg";
+ if (out_texture)
+ *out_texture = LoadImage(CTextureCache::GetCachedPath(m_details.file), width, height, additional_info);
+ CLog::Log(LOGDEBUG, "Fast %s image '%s' to '%s': %p", m_oldHash.IsEmpty() ? "Caching" : "Recaching", image.c_str(), m_details.file.c_str(), out_texture);
+ return true;
+ }
+#endif
CBaseTexture *texture = LoadImage(image, width, height, additional_info);
if (texture)
{
void CDVDPlayer::SetAudioStream(int iStream)
{
+ CMediaSettings::Get().GetCurrentVideoSettings().m_AudioStream = iStream;
m_messenger.Put(new CDVDMsgPlayerSetAudioStream(iStream));
SynchronizeDemuxer(100);
}
/* audio normally won't consume full cpu, so let it have prio */
m_dvdPlayerAudio.SetPriority(GetPriority()+1);
-
+ CMediaSettings::Get().GetCurrentVideoSettings().m_AudioStream = m_SelectionStreams.IndexOf(STREAM_AUDIO, source, iStream);
return true;
}
SRCS += OMXPlayerAudio.cpp
SRCS += OMXPlayerVideo.cpp
SRCS += OMXImage.cpp
+SRCS += PCMRemap.cpp
LIB = omxplayer.a
using namespace std;
-#define OMX_MAX_CHANNELS 10
-
-static enum AEChannel OMXChannelMap[OMX_MAX_CHANNELS] =
-{
- AE_CH_FL , AE_CH_FR,
- AE_CH_FC , AE_CH_LFE,
- AE_CH_BL , AE_CH_BR,
- AE_CH_SL , AE_CH_SR,
- AE_CH_BC , AE_CH_RAW
-};
-
-static enum OMX_AUDIO_CHANNELTYPE OMXChannels[OMX_MAX_CHANNELS] =
-{
- OMX_AUDIO_ChannelLF, OMX_AUDIO_ChannelRF,
- OMX_AUDIO_ChannelCF, OMX_AUDIO_ChannelLFE,
- OMX_AUDIO_ChannelLR, OMX_AUDIO_ChannelRR,
- OMX_AUDIO_ChannelLS, OMX_AUDIO_ChannelRS,
- OMX_AUDIO_ChannelCS, OMX_AUDIO_ChannelNone
-};
-
-static unsigned int WAVEChannels[OMX_MAX_CHANNELS] =
-{
- SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
- SPEAKER_TOP_FRONT_CENTER, SPEAKER_LOW_FREQUENCY,
- SPEAKER_BACK_LEFT, SPEAKER_BACK_RIGHT,
- SPEAKER_SIDE_LEFT, SPEAKER_SIDE_RIGHT,
- SPEAKER_BACK_CENTER, 0
-};
-
static const uint16_t AC3Bitrates[] = {32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 576, 640};
static const uint16_t AC3FSCod [] = {48000, 44100, 32000, 0};
static const uint16_t DTSFSCod [] = {0, 8000, 16000, 32000, 0, 0, 11025, 22050, 44100, 0, 0, 12000, 24000, 48000, 0, 0};
-// 7.1 downmixing coefficients
-const float downmixing_coefficients_8[OMX_AUDIO_MAXCHANNELS] = {
- // L R
- /* L */ 1, 0,
- /* R */ 0, 1,
- /* C */ 0.7071, 0.7071,
- /* LFE */ 0.7071, 0.7071,
- /* Ls */ 0.7071, 0,
- /* Rs */ 0, 0.7071,
- /* Lr */ 0.7071, 0,
- /* Rr */ 0, 0.7071
-};
-
-// 7.1 downmixing coefficients with boosted centre channel
-const float downmixing_coefficients_8_boostcentre[OMX_AUDIO_MAXCHANNELS] = {
- // L R
- /* L */ 0.7071, 0,
- /* R */ 0, 0.7071,
- /* C */ 1, 1,
- /* LFE */ 0.7071, 0.7071,
- /* Ls */ 0.7071, 0,
- /* Rs */ 0, 0.7071,
- /* Lr */ 0.7071, 0,
- /* Rr */ 0, 0.7071
-};
-
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
m_BytesPerSec (0 ),
m_BufferLen (0 ),
m_ChunkLen (0 ),
+ m_InputChannels (0 ),
+ m_OutputChannels (0 ),
m_BitsPerSample (0 ),
m_maxLevel (0.0f ),
m_amplification (1.0f ),
m_attenuation (1.0f ),
- m_desired_attenuation(1.0f),
+ m_submitted (0.0f ),
m_omx_clock (NULL ),
m_av_clock (NULL ),
m_settings_changed(false ),
_aligned_free(m_vizBuffer);
}
-
-CAEChannelInfo COMXAudio::GetChannelLayout(AEAudioFormat format)
-{
- unsigned int count = 0;
-
- if(format.m_dataFormat == AE_FMT_AC3 ||
- format.m_dataFormat == AE_FMT_DTS ||
- format.m_dataFormat == AE_FMT_EAC3)
- count = 2;
- else if (format.m_dataFormat == AE_FMT_TRUEHD ||
- format.m_dataFormat == AE_FMT_DTSHD)
- count = 8;
- else
- {
- for (unsigned int c = 0; c < 8; ++c)
- {
- for (unsigned int i = 0; i < format.m_channelLayout.Count(); ++i)
- {
- if (format.m_channelLayout[i] == OMXChannelMap[c])
- {
- count = c + 1;
- break;
- }
- }
- }
- }
-
- CAEChannelInfo info;
- for (unsigned int i = 0; i < count; ++i)
- info += OMXChannelMap[i];
-
- return info;
-}
-
-
bool COMXAudio::PortSettingsChanged()
{
CSingleLock lock (m_critSection);
}
SetDynamicRangeCompression((long)(CMediaSettings::Get().GetCurrentVideoSettings().m_VolumeAmplification * 100));
- ApplyVolume();
+ UpdateAttenuation();
if( m_omx_mixer.IsInitialized() )
{
return false;
}
- /* mixer output is always stereo */
- m_pcm_output.eChannelMapping[0] = OMX_AUDIO_ChannelLF;
- m_pcm_output.eChannelMapping[1] = OMX_AUDIO_ChannelRF;
- m_pcm_output.nChannels = 2;
+ memcpy(m_pcm_output.eChannelMapping, m_output_channels, sizeof(m_output_channels));
+ // round up to power of 2
+ m_pcm_output.nChannels = m_OutputChannels > 4 ? 8 : m_OutputChannels > 2 ? 4 : m_OutputChannels;
/* limit samplerate (through resampling) if requested */
m_pcm_output.nSamplingRate = std::min((int)m_pcm_output.nSamplingRate, CSettings::Get().GetInt("audiooutput.samplerate"));
return true;
}
-bool COMXAudio::Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo &hints, bool bUsePassthrough, bool bUseHWDecode)
+static unsigned count_bits(int64_t value)
+{
+ unsigned bits = 0;
+ for(;value;++bits)
+ value &= value - 1;
+ return bits;
+}
+
+bool COMXAudio::Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo &hints, uint64_t channelMap, bool bUsePassthrough, bool bUseHWDecode)
{
CSingleLock lock (m_critSection);
OMX_ERRORTYPE omx_err;
m_HWDecode = bUseHWDecode;
m_Passthrough = bUsePassthrough;
+ m_InputChannels = count_bits(channelMap);
m_format = format;
+ enum PCMLayout layout = (enum PCMLayout)std::max(0, CSettings::Get().GetInt("audiooutput.channels")-1);
- if(m_format.m_channelLayout.Count() == 0)
+ if(m_InputChannels == 0)
return false;
if(hints.samplerate == 0)
m_drc = 0;
memset(m_input_channels, 0x0, sizeof(m_input_channels));
+ memset(m_output_channels, 0x0, sizeof(m_output_channels));
memset(&m_wave_header, 0x0, sizeof(m_wave_header));
- for(int i = 0; i < OMX_AUDIO_MAXCHANNELS; i++)
- {
- m_pcm_input.eChannelMapping[i] = OMX_AUDIO_ChannelNone;
- m_input_channels[i] = OMX_AUDIO_ChannelMax;
- }
-
- m_input_channels[0] = OMX_AUDIO_ChannelLF;
- m_input_channels[1] = OMX_AUDIO_ChannelRF;
- m_input_channels[2] = OMX_AUDIO_ChannelMax;
-
m_wave_header.Format.nChannels = 2;
m_wave_header.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
if (!m_Passthrough)
{
- /* setup input channel map */
- int map = 0;
- int chan = 0;
-
- for (unsigned int ch = 0; ch < m_format.m_channelLayout.Count(); ++ch)
- {
- for(map = 0; map < OMX_MAX_CHANNELS; ++map)
- {
- if (m_format.m_channelLayout[ch] == OMXChannelMap[map])
- {
- m_input_channels[chan] = OMXChannels[map];
- m_wave_header.dwChannelMask |= WAVEChannels[map];
- chan++;
- break;
- }
- }
- }
-
- m_vizRemap.Initialize(m_format.m_channelLayout, CAEChannelInfo(AE_CH_LAYOUT_2_0), false, true);
+ enum PCMChannels inLayout[OMX_AUDIO_MAXCHANNELS];
+ enum PCMChannels outLayout[OMX_AUDIO_MAXCHANNELS];
+ // force out layout to stereo if input is not multichannel - it gives the receiver a chance to upmix
+ if (channelMap == (AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT) || channelMap == AV_CH_FRONT_CENTER)
+ layout = PCM_LAYOUT_2_0;
+ BuildChannelMap(inLayout, channelMap);
+ m_OutputChannels = BuildChannelMapCEA(outLayout, GetChannelLayout(layout));
+ CPCMRemap m_remap;
+ m_remap.Reset();
+ /*outLayout = */m_remap.SetInputFormat (m_InputChannels, inLayout, CAEUtil::DataFormatToBits(m_format.m_dataFormat) / 8, m_format.m_sampleRate);
+ m_remap.SetOutputFormat(m_OutputChannels, outLayout);
+ m_remap.GetDownmixMatrix(m_downmix_matrix);
+ m_wave_header.dwChannelMask = channelMap;
+ BuildChannelMapOMX(m_input_channels, channelMap);
+ BuildChannelMapOMX(m_output_channels, GetChannelLayout(layout));
+
+ m_vizRemap.Initialize(GetAEChannelLayout(channelMap), CAEChannelInfo(AE_CH_LAYOUT_2_0), false, true);
}
- OMX_INIT_STRUCTURE(m_pcm_input);
-
- memcpy(m_pcm_input.eChannelMapping, m_input_channels, sizeof(m_input_channels));
-
m_SampleRate = m_format.m_sampleRate;
m_BitsPerSample = CAEUtil::DataFormatToBits(m_format.m_dataFormat);
- m_BufferLen = m_BytesPerSec = m_format.m_sampleRate * (16 >> 3) * m_format.m_channelLayout.Count();
+ m_BufferLen = m_BytesPerSec = m_format.m_sampleRate * (16 >> 3) * m_InputChannels;
m_BufferLen *= AUDIO_BUFFER_SECONDS;
// the audio_decode output buffer size is 32K, and typically we convert from
// 6 channel 32bpp float to 8 channel 16bpp in, so a full 48K input buffer will fit the outbut buffer
m_ChunkLen = 48*1024;
m_wave_header.Samples.wSamplesPerBlock = 0;
- m_wave_header.Format.nChannels = m_format.m_channelLayout.Count();
- m_wave_header.Format.nBlockAlign = m_format.m_channelLayout.Count() *
- (m_BitsPerSample >> 3);
+ m_wave_header.Format.nChannels = m_InputChannels;
+ m_wave_header.Format.nBlockAlign = m_InputChannels * (m_BitsPerSample >> 3);
// 0x8000 is custom format interpreted by GPU as WAVE_FORMAT_IEEE_FLOAT_PLANAR
m_wave_header.Format.wFormatTag = m_BitsPerSample == 32 ? 0x8000 : WAVE_FORMAT_PCM;
m_wave_header.Format.nSamplesPerSec = m_format.m_sampleRate;
m_wave_header.Format.cbSize = 0;
m_wave_header.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ OMX_INIT_STRUCTURE(m_pcm_input);
+ memcpy(m_pcm_input.eChannelMapping, m_input_channels, sizeof(m_input_channels));
m_pcm_input.eNumData = OMX_NumericalDataSigned;
m_pcm_input.eEndian = OMX_EndianLittle;
m_pcm_input.bInterleaved = OMX_TRUE;
m_pcm_input.nBitPerSample = m_BitsPerSample;
m_pcm_input.ePCMMode = OMX_AUDIO_PCMModeLinear;
- m_pcm_input.nChannels = m_format.m_channelLayout.Count();
+ m_pcm_input.nChannels = m_InputChannels;
m_pcm_input.nSamplingRate = m_format.m_sampleRate;
if(!m_omx_decoder.Initialize("OMX.broadcom.audio_decode", OMX_IndexParamAudioInit))
m_submitted_eos = false;
m_failed_eos = false;
m_last_pts = DVD_NOPTS_VALUE;
+ m_submitted = 0.0f;
+ m_maxLevel = 0.0f;
CLog::Log(LOGDEBUG, "COMXAudio::Initialize Input bps %d samplerate %d channels %d buffer size %d bytes per second %d",
(int)m_pcm_input.nBitPerSample, (int)m_pcm_input.nSamplingRate, (int)m_pcm_input.nChannels, m_BufferLen, m_BytesPerSec);
m_dllAvUtil.Unload();
+ while(!m_ampqueue.empty())
+ m_ampqueue.pop_front();
+
m_last_pts = DVD_NOPTS_VALUE;
+ m_submitted = 0.0f;
+ m_maxLevel = 0.0f;
/* dummy call to inform PiAudioAE that audo is inactive */
CAEFactory::FreeStream(0);
m_omx_render_hdmi.FlushAll();
m_last_pts = DVD_NOPTS_VALUE;
+ m_submitted = 0.0f;
+ m_maxLevel = 0.0f;
m_LostSync = true;
m_setStartTime = true;
}
CSingleLock lock (m_critSection);
m_amplification = powf(10.0f, (float)drc / 2000.0f);
if (m_settings_changed)
- ApplyVolume();
+ UpdateAttenuation();
}
//***********************************************************************************************
CSingleLock lock (m_critSection);
m_Mute = bMute;
if (m_settings_changed)
- ApplyVolume();
+ UpdateAttenuation();
}
//***********************************************************************************************
CSingleLock lock (m_critSection);
m_CurrentVolume = fVolume;
if (m_settings_changed)
- ApplyVolume();
+ UpdateAttenuation();
}
//***********************************************************************************************
// the analogue volume is too quiet for some. Allow use of an advancedsetting to boost this (at risk of distortion) (deprecated)
double gain = pow(10, (g_advancedSettings.m_ac3Gain - 12.0f) / 20.0);
- double r = 1.0;
- const float* coeff = downmixing_coefficients_8;
- // alternate coffeciciants that boost centre channel more
- if(!CSettings::Get().GetBool("audiooutput.boostcentre") && m_format.m_channelLayout.Count() > 2)
- coeff = downmixing_coefficients_8_boostcentre;
+ const float* coeff = m_downmix_matrix;
- // normally we normalise the levels, can be skipped (boosted) at risk of distortion
- if(!CSettings::Get().GetBool("audiooutput.normalizelevels"))
- {
- double sum_L = 0;
- double sum_R = 0;
- for(size_t i = 0; i < OMX_AUDIO_MAXCHANNELS; ++i)
- {
- if (m_input_channels[i] == OMX_AUDIO_ChannelMax)
- break;
- if(i & 1)
- sum_R += coeff[i];
- else
- sum_L += coeff[i];
- }
-
- r /= max(sum_L, sum_R);
- }
- r *= gain;
-
- OMX_CONFIG_BRCMAUDIODOWNMIXCOEFFICIENTS mix;
+ OMX_CONFIG_BRCMAUDIODOWNMIXCOEFFICIENTS8x8 mix;
OMX_INIT_STRUCTURE(mix);
OMX_ERRORTYPE omx_err;
- assert(sizeof(mix.coeff)/sizeof(mix.coeff[0]) == 16);
+ assert(sizeof(mix.coeff)/sizeof(mix.coeff[0]) == 64);
if (m_amplification != 1.0)
{
// reduce scaling so overflow can be seen
- for(size_t i = 0; i < 16; ++i)
- mix.coeff[i] = static_cast<unsigned int>(0x10000 * (coeff[i] * r * 0.01f));
+ for(size_t i = 0; i < 8*8; ++i)
+ mix.coeff[i] = static_cast<unsigned int>(0x10000 * (coeff[i] * gain * 0.01f));
mix.nPortIndex = m_omx_decoder.GetInputPort();
- omx_err = m_omx_decoder.SetConfig(OMX_IndexConfigBrcmAudioDownmixCoefficients, &mix);
+ omx_err = m_omx_decoder.SetConfig(OMX_IndexConfigBrcmAudioDownmixCoefficients8x8, &mix);
if(omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "%s::%s - error setting decoder OMX_IndexConfigBrcmAudioDownmixCoefficients, error 0x%08x\n",
return false;
}
}
- for(size_t i = 0; i < 16; ++i)
- mix.coeff[i] = static_cast<unsigned int>(0x10000 * (coeff[i] * r * fVolume * m_amplification * m_attenuation));
+ for(size_t i = 0; i < 8*8; ++i)
+ mix.coeff[i] = static_cast<unsigned int>(0x10000 * (coeff[i] * gain * fVolume * m_amplification * m_attenuation));
mix.nPortIndex = m_omx_mixer.GetInputPort();
- omx_err = m_omx_mixer.SetConfig(OMX_IndexConfigBrcmAudioDownmixCoefficients, &mix);
+ omx_err = m_omx_mixer.SetConfig(OMX_IndexConfigBrcmAudioDownmixCoefficients8x8, &mix);
if(omx_err != OMX_ErrorNone)
{
CLog::Log(LOGERROR, "%s::%s - error setting mixer OMX_IndexConfigBrcmAudioDownmixCoefficients, error 0x%08x\n",
unsigned int vizBufferSamples = len / (CAEUtil::DataFormatToBits(m_format.m_dataFormat) >> 3);
/* input frames */
- unsigned int frames = vizBufferSamples / m_format.m_channelLayout.Count();
+ unsigned int frames = vizBufferSamples / m_InputChannels;
float *floatBuffer = (float *)data;
if (m_format.m_dataFormat != AE_FMT_FLOAT)
m_vizRemap.Remap(floatBuffer, (float*)m_vizRemapBuffer, frames);
/* output samples */
- vizBufferSamples = vizBufferSamples / m_format.m_channelLayout.Count() * 2;
+ vizBufferSamples = vizBufferSamples / m_InputChannels * 2;
/* viz size is limited */
if(vizBufferSamples > VIS_PACKET_SIZE)
return len;
}
- int m_InputChannels = m_format.m_channelLayout.Count();
unsigned pitch = (m_Passthrough || m_HWDecode) ? 1:(m_BitsPerSample >> 3) * m_InputChannels;
unsigned int demuxer_samples = len / pitch;
unsigned int demuxer_samples_sent = 0;
uint8_t *dst = omx_buffer->pBuffer;
uint8_t *src = demuxer_content + demuxer_samples_sent * (m_BitsPerSample >> 3);
// we need to extract samples from planar audio, so the copying needs to be done per plane
- for (int i=0; i<m_InputChannels; i++)
+ for (int i=0; i<(int)m_InputChannels; i++)
{
memcpy(dst, src, omx_buffer->nFilledLen / m_InputChannels);
dst += omx_buffer->nFilledLen / m_InputChannels;
}
}
}
-
+ m_submitted += (float)demuxer_samples / m_SampleRate;
if (m_amplification != 1.0)
+ UpdateAttenuation();
+ return len;
+}
+
+void COMXAudio::UpdateAttenuation()
+{
+ if (m_amplification == 1.0)
{
- double level_pts = 0.0;
- float level = GetMaxLevel(level_pts);
- if (level_pts != 0.0)
- {
- float alpha_h = -1.0f/(0.025f*log10f(0.999f));
- float alpha_r = -1.0f/(0.100f*log10f(0.900f));
- float hold = powf(10.0f, -1.0f / (alpha_h * g_advancedSettings.m_limiterHold));
- float release = powf(10.0f, -1.0f / (alpha_r * g_advancedSettings.m_limiterRelease));
- m_maxLevel = level > m_maxLevel ? level : hold * m_maxLevel + (1.0f-hold) * level;
+ ApplyVolume();
+ return;
+ }
- float amp = m_amplification * m_desired_attenuation;
+ double level_pts = 0.0;
+ float level = GetMaxLevel(level_pts);
+ if (level_pts != 0.0)
+ {
+ amplitudes_t v;
+ v.level = level;
+ v.pts = level_pts;
+ m_ampqueue.push_back(v);
+ }
+ double stamp = m_av_clock->OMXMediaTime();
+ // discard too old data
+ while(!m_ampqueue.empty())
+ {
+ amplitudes_t &v = m_ampqueue.front();
+ /* we'll also consume if queue gets unexpectedly long to avoid filling memory */
+ if (v.pts == DVD_NOPTS_VALUE || v.pts < stamp || v.pts - stamp > DVD_SEC_TO_TIME(15.0))
+ m_ampqueue.pop_front();
+ else break;
+ }
+ float maxlevel = 0.0f, imminent_maxlevel = 0.0f;
+ for (int i=0; i < (int)m_ampqueue.size(); i++)
+ {
+ amplitudes_t &v = m_ampqueue[i];
+ maxlevel = std::max(maxlevel, v.level);
+ // check for maximum volume in next 200ms
+ if (v.pts != DVD_NOPTS_VALUE && v.pts < stamp + DVD_SEC_TO_TIME(0.2))
+ imminent_maxlevel = std::max(imminent_maxlevel, v.level);
+ }
- // want m_maxLevel * amp -> 1.0
- m_desired_attenuation = std::min(1.0f, std::max(m_desired_attenuation / (amp * m_maxLevel), 1.0f/m_amplification));
- m_attenuation = release * m_attenuation + (1.0f-release) * m_desired_attenuation;
+ if (maxlevel != 0.0)
+ {
+ float alpha_h = -1.0f/(0.025f*log10f(0.999f));
+ float alpha_r = -1.0f/(0.100f*log10f(0.900f));
+ float decay = powf(10.0f, -1.0f / (alpha_h * g_advancedSettings.m_limiterHold));
+ float attack = powf(10.0f, -1.0f / (alpha_r * g_advancedSettings.m_limiterRelease));
+ // if we are going to clip imminently then deal with it now
+ if (imminent_maxlevel > m_maxLevel)
+ m_maxLevel = imminent_maxlevel;
+ // clip but not imminently can ramp up more slowly
+ else if (maxlevel > m_maxLevel)
+ m_maxLevel = attack * m_maxLevel + (1.0f-attack) * maxlevel;
+ // not clipping, decay more slowly
+ else
+ m_maxLevel = decay * m_maxLevel + (1.0f-decay ) * maxlevel;
- ApplyVolume();
- }
+ // want m_maxLevel * amp -> 1.0
+ float amp = m_amplification * m_attenuation;
+
+ // We fade in the attenuation over first couple of seconds
+ float start = std::min(std::max((m_submitted-1.0f), 0.0f), 1.0f);
+ float attenuation = std::min(1.0f, std::max(m_attenuation / (amp * m_maxLevel), 1.0f/m_amplification));
+ m_attenuation = (1.0f - start) * 1.0f/m_amplification + start * attenuation;
}
- return len;
+ else
+ {
+ m_attenuation = 1.0f/m_amplification;
+ }
+ ApplyVolume();
}
//***********************************************************************************************
memset(*buffer, 0x0, *oldSize);
}
+void COMXAudio::BuildChannelMap(enum PCMChannels *channelMap, uint64_t layout)
+{
+ int index = 0;
+ if (layout & AV_CH_FRONT_LEFT ) channelMap[index++] = PCM_FRONT_LEFT ;
+ if (layout & AV_CH_FRONT_RIGHT ) channelMap[index++] = PCM_FRONT_RIGHT ;
+ if (layout & AV_CH_FRONT_CENTER ) channelMap[index++] = PCM_FRONT_CENTER ;
+ if (layout & AV_CH_LOW_FREQUENCY ) channelMap[index++] = PCM_LOW_FREQUENCY ;
+ if (layout & AV_CH_BACK_LEFT ) channelMap[index++] = PCM_BACK_LEFT ;
+ if (layout & AV_CH_BACK_RIGHT ) channelMap[index++] = PCM_BACK_RIGHT ;
+ if (layout & AV_CH_FRONT_LEFT_OF_CENTER ) channelMap[index++] = PCM_FRONT_LEFT_OF_CENTER ;
+ if (layout & AV_CH_FRONT_RIGHT_OF_CENTER) channelMap[index++] = PCM_FRONT_RIGHT_OF_CENTER;
+ if (layout & AV_CH_BACK_CENTER ) channelMap[index++] = PCM_BACK_CENTER ;
+ if (layout & AV_CH_SIDE_LEFT ) channelMap[index++] = PCM_SIDE_LEFT ;
+ if (layout & AV_CH_SIDE_RIGHT ) channelMap[index++] = PCM_SIDE_RIGHT ;
+ if (layout & AV_CH_TOP_CENTER ) channelMap[index++] = PCM_TOP_CENTER ;
+ if (layout & AV_CH_TOP_FRONT_LEFT ) channelMap[index++] = PCM_TOP_FRONT_LEFT ;
+ if (layout & AV_CH_TOP_FRONT_CENTER ) channelMap[index++] = PCM_TOP_FRONT_CENTER ;
+ if (layout & AV_CH_TOP_FRONT_RIGHT ) channelMap[index++] = PCM_TOP_FRONT_RIGHT ;
+ if (layout & AV_CH_TOP_BACK_LEFT ) channelMap[index++] = PCM_TOP_BACK_LEFT ;
+ if (layout & AV_CH_TOP_BACK_CENTER ) channelMap[index++] = PCM_TOP_BACK_CENTER ;
+ if (layout & AV_CH_TOP_BACK_RIGHT ) channelMap[index++] = PCM_TOP_BACK_RIGHT ;
+ while (index<OMX_AUDIO_MAXCHANNELS)
+ channelMap[index++] = PCM_INVALID;
+}
+
+// See CEA spec: Table 20, Audio InfoFrame data byte 4 for the ordering here
+int COMXAudio::BuildChannelMapCEA(enum PCMChannels *channelMap, uint64_t layout)
+{
+ int index = 0;
+ if (layout & AV_CH_FRONT_LEFT ) channelMap[index++] = PCM_FRONT_LEFT;
+ if (layout & AV_CH_FRONT_RIGHT ) channelMap[index++] = PCM_FRONT_RIGHT;
+ if (layout & AV_CH_LOW_FREQUENCY ) channelMap[index++] = PCM_LOW_FREQUENCY;
+ if (layout & AV_CH_FRONT_CENTER ) channelMap[index++] = PCM_FRONT_CENTER;
+ if (layout & AV_CH_BACK_LEFT ) channelMap[index++] = PCM_BACK_LEFT;
+ if (layout & AV_CH_BACK_RIGHT ) channelMap[index++] = PCM_BACK_RIGHT;
+ if (layout & AV_CH_SIDE_LEFT ) channelMap[index++] = PCM_SIDE_LEFT;
+ if (layout & AV_CH_SIDE_RIGHT ) channelMap[index++] = PCM_SIDE_RIGHT;
+
+ while (index<OMX_AUDIO_MAXCHANNELS)
+ channelMap[index++] = PCM_INVALID;
+
+ int num_channels = 0;
+ for (index=0; index<OMX_AUDIO_MAXCHANNELS; index++)
+ if (channelMap[index] != PCM_INVALID)
+ num_channels = index+1;
+ return num_channels;
+}
+
+void COMXAudio::BuildChannelMapOMX(enum OMX_AUDIO_CHANNELTYPE * channelMap, uint64_t layout)
+{
+ int index = 0;
+
+ if (layout & AV_CH_FRONT_LEFT ) channelMap[index++] = OMX_AUDIO_ChannelLF;
+ if (layout & AV_CH_FRONT_RIGHT ) channelMap[index++] = OMX_AUDIO_ChannelRF;
+ if (layout & AV_CH_FRONT_CENTER ) channelMap[index++] = OMX_AUDIO_ChannelCF;
+ if (layout & AV_CH_LOW_FREQUENCY ) channelMap[index++] = OMX_AUDIO_ChannelLFE;
+ if (layout & AV_CH_BACK_LEFT ) channelMap[index++] = OMX_AUDIO_ChannelLR;
+ if (layout & AV_CH_BACK_RIGHT ) channelMap[index++] = OMX_AUDIO_ChannelRR;
+ if (layout & AV_CH_SIDE_LEFT ) channelMap[index++] = OMX_AUDIO_ChannelLS;
+ if (layout & AV_CH_SIDE_RIGHT ) channelMap[index++] = OMX_AUDIO_ChannelRS;
+ if (layout & AV_CH_BACK_CENTER ) channelMap[index++] = OMX_AUDIO_ChannelCS;
+ // following are not in openmax spec, but gpu does accept them
+ if (layout & AV_CH_FRONT_LEFT_OF_CENTER ) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)10;
+ if (layout & AV_CH_FRONT_RIGHT_OF_CENTER) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)11;
+ if (layout & AV_CH_TOP_CENTER ) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)12;
+ if (layout & AV_CH_TOP_FRONT_LEFT ) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)13;
+ if (layout & AV_CH_TOP_FRONT_CENTER ) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)14;
+ if (layout & AV_CH_TOP_FRONT_RIGHT ) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)15;
+ if (layout & AV_CH_TOP_BACK_LEFT ) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)16;
+ if (layout & AV_CH_TOP_BACK_CENTER ) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)17;
+ if (layout & AV_CH_TOP_BACK_RIGHT ) channelMap[index++] = (enum OMX_AUDIO_CHANNELTYPE)18;
+
+ while (index<OMX_AUDIO_MAXCHANNELS)
+ channelMap[index++] = OMX_AUDIO_ChannelNone;
+}
+
+uint64_t COMXAudio::GetChannelLayout(enum PCMLayout layout)
+{
+ uint64_t layouts[] = {
+ /* 2.0 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT,
+ /* 2.1 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_LOW_FREQUENCY,
+ /* 3.0 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_FRONT_CENTER,
+ /* 3.1 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_FRONT_CENTER | 1<<PCM_LOW_FREQUENCY,
+ /* 4.0 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_BACK_LEFT | 1<<PCM_BACK_RIGHT,
+ /* 4.1 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_BACK_LEFT | 1<<PCM_BACK_RIGHT | 1<<PCM_LOW_FREQUENCY,
+ /* 5.0 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_FRONT_CENTER | 1<<PCM_BACK_LEFT | 1<<PCM_BACK_RIGHT,
+ /* 5.1 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_FRONT_CENTER | 1<<PCM_BACK_LEFT | 1<<PCM_BACK_RIGHT | 1<<PCM_LOW_FREQUENCY,
+ /* 7.0 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_FRONT_CENTER | 1<<PCM_SIDE_LEFT | 1<<PCM_SIDE_RIGHT | 1<<PCM_BACK_LEFT | 1<<PCM_BACK_RIGHT,
+ /* 7.1 */ 1<<PCM_FRONT_LEFT | 1<<PCM_FRONT_RIGHT | 1<<PCM_FRONT_CENTER | 1<<PCM_SIDE_LEFT | 1<<PCM_SIDE_RIGHT | 1<<PCM_BACK_LEFT | 1<<PCM_BACK_RIGHT | 1<<PCM_LOW_FREQUENCY
+ };
+ return (int)layout < 10 ? layouts[(int)layout] : 0;
+}
+
+CAEChannelInfo COMXAudio::GetAEChannelLayout(uint64_t layout)
+{
+ CAEChannelInfo m_channelLayout;
+ m_channelLayout.Reset();
+
+ if (layout & AV_CH_FRONT_LEFT ) m_channelLayout += AE_CH_FL ;
+ if (layout & AV_CH_FRONT_RIGHT ) m_channelLayout += AE_CH_FR ;
+ if (layout & AV_CH_FRONT_CENTER ) m_channelLayout += AE_CH_FC ;
+ if (layout & AV_CH_LOW_FREQUENCY ) m_channelLayout += AE_CH_LFE ;
+ if (layout & AV_CH_BACK_LEFT ) m_channelLayout += AE_CH_BL ;
+ if (layout & AV_CH_BACK_RIGHT ) m_channelLayout += AE_CH_BR ;
+ if (layout & AV_CH_FRONT_LEFT_OF_CENTER ) m_channelLayout += AE_CH_FLOC;
+ if (layout & AV_CH_FRONT_RIGHT_OF_CENTER) m_channelLayout += AE_CH_FROC;
+ if (layout & AV_CH_BACK_CENTER ) m_channelLayout += AE_CH_BC ;
+ if (layout & AV_CH_SIDE_LEFT ) m_channelLayout += AE_CH_SL ;
+ if (layout & AV_CH_SIDE_RIGHT ) m_channelLayout += AE_CH_SR ;
+ if (layout & AV_CH_TOP_CENTER ) m_channelLayout += AE_CH_TC ;
+ if (layout & AV_CH_TOP_FRONT_LEFT ) m_channelLayout += AE_CH_TFL ;
+ if (layout & AV_CH_TOP_FRONT_CENTER ) m_channelLayout += AE_CH_TFC ;
+ if (layout & AV_CH_TOP_FRONT_RIGHT ) m_channelLayout += AE_CH_TFR ;
+ if (layout & AV_CH_TOP_BACK_LEFT ) m_channelLayout += AE_CH_BL ;
+ if (layout & AV_CH_TOP_BACK_CENTER ) m_channelLayout += AE_CH_BC ;
+ if (layout & AV_CH_TOP_BACK_RIGHT ) m_channelLayout += AE_CH_BR ;
+ return m_channelLayout;
+}
#include "OMXCore.h"
#include "DllAvCodec.h"
#include "DllAvUtil.h"
+#include "PCMRemap.h"
#include "threads/CriticalSection.h"
float GetCacheTime();
float GetCacheTotal();
COMXAudio();
- bool Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo &hints, bool bUsePassthrough, bool bUseHWDecode);
+ bool Initialize(AEAudioFormat format, OMXClock *clock, CDVDStreamInfo &hints, uint64_t channelMap, bool bUsePassthrough, bool bUseHWDecode);
bool PortSettingsChanged();
~COMXAudio();
void PrintDTS(OMX_AUDIO_PARAM_DTSTYPE *dtsparam);
unsigned int SyncDTS(BYTE* pData, unsigned int iSize);
unsigned int SyncAC3(BYTE* pData, unsigned int iSize);
+ void UpdateAttenuation();
bool BadState() { return !m_Initialized; };
unsigned int GetAudioRenderingLatency();
float GetMaxLevel(double &pts);
void VizPacket(const void* data, unsigned int len, double pts);
+ void BuildChannelMap(enum PCMChannels *channelMap, uint64_t layout);
+ int BuildChannelMapCEA(enum PCMChannels *channelMap, uint64_t layout);
+ void BuildChannelMapOMX(enum OMX_AUDIO_CHANNELTYPE *channelMap, uint64_t layout);
+ uint64_t GetChannelLayout(enum PCMLayout layout);
+ CAEChannelInfo GetAEChannelLayout(uint64_t layout);
+
private:
IAudioCallback* m_pCallback;
bool m_Initialized;
unsigned int m_BytesPerSec;
unsigned int m_BufferLen;
unsigned int m_ChunkLen;
+ unsigned int m_InputChannels;
+ unsigned int m_OutputChannels;
unsigned int m_BitsPerSample;
float m_maxLevel;
float m_amplification;
float m_attenuation;
- float m_desired_attenuation;
+ float m_submitted;
COMXCoreComponent *m_omx_clock;
OMXClock *m_av_clock;
bool m_settings_changed;
} vizblock_t;
std::queue<vizblock_t> m_vizqueue;
+ typedef struct {
+ double pts;
+ float level;
+ } amplitudes_t;
+ std::deque<amplitudes_t> m_ampqueue;
+
+ float m_downmix_matrix[OMX_AUDIO_MAXCHANNELS*OMX_AUDIO_MAXCHANNELS];
+
+ OMX_AUDIO_CHANNELTYPE m_input_channels[OMX_AUDIO_MAXCHANNELS];
+ OMX_AUDIO_CHANNELTYPE m_output_channels[OMX_AUDIO_MAXCHANNELS];
OMX_AUDIO_PARAM_PCMMODETYPE m_pcm_output;
OMX_AUDIO_PARAM_PCMMODETYPE m_pcm_input;
OMX_AUDIO_PARAM_DTSTYPE m_dtsParam;
COMXCoreTunel m_omx_tunnel_splitter_hdmi;
DllAvUtil m_dllAvUtil;
- OMX_AUDIO_CHANNELTYPE m_input_channels[OMX_AUDIO_MAXCHANNELS];
-
- CAEChannelInfo m_channelLayout;
-
- static CAEChannelInfo GetChannelLayout(AEAudioFormat format);
-
static void CheckOutputBufferSize(void **buffer, int *oldSize, int newSize);
CCriticalSection m_critSection;
};
m_bOpenedCodec = false;
m_channels = 0;
- m_layout = 0;
m_pFrame1 = NULL;
m_bGotFrame = false;
m_iSampleFormat = AV_SAMPLE_FMT_NONE;
if(m_pCodecContext->sample_fmt != m_desiredSampleFormat)
{
if(m_pConvert && (m_pCodecContext->sample_fmt != m_iSampleFormat || m_channels != m_pCodecContext->channels))
+ {
m_dllSwResample.swr_free(&m_pConvert);
+ m_channels = m_pCodecContext->channels;
+ }
if(!m_pConvert)
{
return bits;
}
-void COMXAudioCodecOMX::BuildChannelMap()
+uint64_t COMXAudioCodecOMX::GetChannelMap()
{
- if (m_channels == m_pCodecContext->channels && m_layout == m_pCodecContext->channel_layout)
- return; //nothing to do here
-
- m_channels = m_pCodecContext->channels;
- m_layout = m_pCodecContext->channel_layout;
-
- int64_t layout;
-
+ uint64_t layout;
int bits = count_bits(m_pCodecContext->channel_layout);
if (bits == m_pCodecContext->channels)
layout = m_pCodecContext->channel_layout;
CLog::Log(LOGINFO, "COMXAudioCodecOMX::GetChannelMap - FFmpeg reported %d channels, but the layout contains %d ignoring", m_pCodecContext->channels, bits);
layout = m_dllAvUtil.av_get_default_channel_layout(m_pCodecContext->channels);
}
-
- m_channelLayout.Reset();
-
- if (layout & AV_CH_FRONT_LEFT ) m_channelLayout += AE_CH_FL ;
- if (layout & AV_CH_FRONT_RIGHT ) m_channelLayout += AE_CH_FR ;
- if (layout & AV_CH_FRONT_CENTER ) m_channelLayout += AE_CH_FC ;
- if (layout & AV_CH_LOW_FREQUENCY ) m_channelLayout += AE_CH_LFE ;
- if (layout & AV_CH_BACK_LEFT ) m_channelLayout += AE_CH_BL ;
- if (layout & AV_CH_BACK_RIGHT ) m_channelLayout += AE_CH_BR ;
- if (layout & AV_CH_FRONT_LEFT_OF_CENTER ) m_channelLayout += AE_CH_FLOC;
- if (layout & AV_CH_FRONT_RIGHT_OF_CENTER) m_channelLayout += AE_CH_FROC;
- if (layout & AV_CH_BACK_CENTER ) m_channelLayout += AE_CH_BC ;
- if (layout & AV_CH_SIDE_LEFT ) m_channelLayout += AE_CH_SL ;
- if (layout & AV_CH_SIDE_RIGHT ) m_channelLayout += AE_CH_SR ;
- if (layout & AV_CH_TOP_CENTER ) m_channelLayout += AE_CH_TC ;
- if (layout & AV_CH_TOP_FRONT_LEFT ) m_channelLayout += AE_CH_TFL ;
- if (layout & AV_CH_TOP_FRONT_CENTER ) m_channelLayout += AE_CH_TFC ;
- if (layout & AV_CH_TOP_FRONT_RIGHT ) m_channelLayout += AE_CH_TFR ;
- if (layout & AV_CH_TOP_BACK_LEFT ) m_channelLayout += AE_CH_BL ;
- if (layout & AV_CH_TOP_BACK_CENTER ) m_channelLayout += AE_CH_BC ;
- if (layout & AV_CH_TOP_BACK_RIGHT ) m_channelLayout += AE_CH_BR ;
-}
-
-CAEChannelInfo COMXAudioCodecOMX::GetChannelMap()
-{
- BuildChannelMap();
- return m_channelLayout;
+ return layout;
}
int GetData(BYTE** dst);
void Reset();
int GetChannels();
- virtual CAEChannelInfo GetChannelMap();
+ uint64_t GetChannelMap();
int GetSampleRate();
int GetBitsPerSample();
static const char* GetName() { return "FFmpeg"; }
SwrContext* m_pConvert;
enum AVSampleFormat m_iSampleFormat;
enum AVSampleFormat m_desiredSampleFormat;
- CAEChannelInfo m_channelLayout;
AVFrame* m_pFrame1;
bool m_bOpenedCodec;
int m_channels;
- uint64_t m_layout;
bool m_bFirstFrame;
bool m_bGotFrame;
DllAvCodec m_dllAvCodec;
DllAvUtil m_dllAvUtil;
DllSwResample m_dllSwResample;
-
- void BuildChannelMap();
};
#include "settings/DisplaySettings.h"
#include "settings/Settings.h"
#include "linux/RBP.h"
+#include "utils/URIUtils.h"
#define EXIF_TAG_ORIENTATION 0x0112
return clamped;
}
+bool COMXImage::CreateThumb(const CStdString& srcFile, unsigned int maxHeight, unsigned int maxWidth, std::string &additional_info, const CStdString& destFile)
+{
+ bool okay = false;
+ COMXImageFile file;
+ COMXImageReEnc reenc;
+ void *pDestBuffer;
+ unsigned int nDestSize;
+ if (URIUtils::HasExtension(srcFile, ".jpg|.tbn") && file.ReadFile(srcFile) && reenc.ReEncode(file, maxWidth, maxHeight, pDestBuffer, nDestSize))
+ {
+ XFILE::CFile outfile;
+ if (outfile.OpenForWrite(destFile, true))
+ {
+ outfile.Write(pDestBuffer, nDestSize);
+ outfile.Close();
+ okay = true;
+ }
+ else
+ CLog::Log(LOGERROR, "%s: can't open output file: %s\n", __func__, destFile.c_str());
+ }
+ return okay;
+}
+
#ifdef CLASSNAME
#undef CLASSNAME
#endif
return false;
}
+
+#ifdef CLASSNAME
+#undef CLASSNAME
+#endif
+#define CLASSNAME "COMXReEnc"
+
+COMXImageReEnc::COMXImageReEnc()
+{
+ m_encoded_buffer = NULL;
+ m_pDestBuffer = NULL;
+ m_nDestAllocSize = 0;
+}
+
+COMXImageReEnc::~COMXImageReEnc()
+{
+ Close();
+ if (m_pDestBuffer)
+ free (m_pDestBuffer);
+ m_pDestBuffer = NULL;
+ m_nDestAllocSize = 0;
+}
+
+void COMXImageReEnc::Close()
+{
+ CSingleLock lock(m_OMXSection);
+
+ if(m_omx_decoder.IsInitialized())
+ {
+ m_omx_decoder.FlushInput();
+ m_omx_decoder.FreeInputBuffers();
+ }
+ if(m_omx_encoder.IsInitialized())
+ {
+ m_omx_encoder.FlushOutput();
+ m_omx_encoder.FreeOutputBuffers();
+ }
+ if(m_omx_tunnel_decode.IsInitialized())
+ m_omx_tunnel_decode.Deestablish();
+ if(m_omx_tunnel_resize.IsInitialized())
+ m_omx_tunnel_resize.Deestablish();
+ if(m_omx_decoder.IsInitialized())
+ m_omx_decoder.Deinitialize(true);
+ if(m_omx_resize.IsInitialized())
+ m_omx_resize.Deinitialize(true);
+ if(m_omx_encoder.IsInitialized())
+ m_omx_encoder.Deinitialize(true);
+}
+
+
+
+bool COMXImageReEnc::HandlePortSettingChange(unsigned int resize_width, unsigned int resize_height, bool port_settings_changed)
+{
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+ // on the first port settings changed event, we create the tunnel and alloc the buffer
+ if (!port_settings_changed)
+ {
+ OMX_PARAM_PORTDEFINITIONTYPE port_def;
+ OMX_INIT_STRUCTURE(port_def);
+
+ port_def.nPortIndex = m_omx_decoder.GetOutputPort();
+ m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &port_def);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_decoder.GetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ // TODO: jpeg decoder can decimate by factors of 2
+ port_def.format.image.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar;
+ port_def.format.image.nSliceHeight = 16;//(port_def.format.image.nFrameHeight+15) & ~15;
+ port_def.format.image.nStride = 0;
+
+ m_omx_decoder.SetParameter(OMX_IndexParamPortDefinition, &port_def);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_decoder.SetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ if(!m_omx_resize.Initialize("OMX.broadcom.resize", OMX_IndexParamImageInit))
+ {
+ CLog::Log(LOGERROR, "%s::%s error m_omx_resize.Initialize\n", CLASSNAME, __func__);
+ return false;
+ }
+
+ port_def.nPortIndex = m_omx_resize.GetInputPort();
+
+ m_omx_resize.SetParameter(OMX_IndexParamPortDefinition, &port_def);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_resize.SetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ port_def.nPortIndex = m_omx_resize.GetOutputPort();
+ m_omx_resize.GetParameter(OMX_IndexParamPortDefinition, &port_def);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_resize.GetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+ port_def.format.image.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar;
+ port_def.format.image.nFrameWidth = resize_width;
+ port_def.format.image.nFrameHeight = resize_height;
+ port_def.format.image.nSliceHeight = (resize_height+15) & ~15;
+ port_def.format.image.nStride = 0;
+ m_omx_resize.SetParameter(OMX_IndexParamPortDefinition, &port_def);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_resize.SetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ if(!m_omx_encoder.Initialize("OMX.broadcom.image_encode", OMX_IndexParamImageInit))
+ {
+ CLog::Log(LOGERROR, "%s::%s error m_omx_encoder.Initialize\n", CLASSNAME, __func__);
+ return false;
+ }
+
+ port_def.nPortIndex = m_omx_encoder.GetInputPort();
+ m_omx_encoder.GetParameter(OMX_IndexParamPortDefinition, &port_def);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_encoder.GetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+ port_def.format.image.eColorFormat = OMX_COLOR_FormatYUV420PackedPlanar;
+ port_def.format.image.nFrameWidth = resize_width;
+ port_def.format.image.nFrameHeight = resize_height;
+ port_def.format.image.nSliceHeight = (resize_height+15) & ~15;
+ port_def.format.image.nStride = 0;
+ m_omx_encoder.SetParameter(OMX_IndexParamPortDefinition, &port_def);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_encoder.SetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ port_def.nPortIndex = m_omx_encoder.GetOutputPort();
+ omx_err = m_omx_encoder.GetParameter(OMX_IndexParamPortDefinition, &port_def);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_encoder.GetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ port_def.format.image.eCompressionFormat = OMX_IMAGE_CodingJPEG;
+ port_def.format.image.eColorFormat = OMX_COLOR_FormatUnused;
+ port_def.format.image.nFrameWidth = resize_width;
+ port_def.format.image.nFrameHeight = resize_height;
+ port_def.format.image.nStride = 0;
+ port_def.format.image.nSliceHeight = 0;
+ port_def.format.image.bFlagErrorConcealment = OMX_FALSE;
+
+ omx_err = m_omx_encoder.SetParameter(OMX_IndexParamPortDefinition, &port_def);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_encoder.SetParameter result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ OMX_IMAGE_PARAM_QFACTORTYPE qfactor;
+ OMX_INIT_STRUCTURE(qfactor);
+ qfactor.nPortIndex = m_omx_encoder.GetOutputPort();
+ qfactor.nQFactor = 16;
+
+ omx_err = m_omx_encoder.SetParameter(OMX_IndexParamQFactor, &qfactor);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_encoder.SetParameter OMX_IndexParamQFactor result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ omx_err = m_omx_encoder.AllocOutputBuffers();
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_encoder.AllocOutputBuffers result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ m_omx_tunnel_decode.Initialize(&m_omx_decoder, m_omx_decoder.GetOutputPort(), &m_omx_resize, m_omx_resize.GetInputPort());
+
+ omx_err = m_omx_tunnel_decode.Establish(false);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_tunnel_decode.Establish\n", CLASSNAME, __func__);
+ return false;
+ }
+
+ m_omx_tunnel_resize.Initialize(&m_omx_resize, m_omx_resize.GetOutputPort(), &m_omx_encoder, m_omx_encoder.GetInputPort());
+
+ omx_err = m_omx_tunnel_resize.Establish(false);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_tunnel_resize.Establish\n", CLASSNAME, __func__);
+ return false;
+ }
+
+ omx_err = m_omx_resize.SetStateForComponent(OMX_StateExecuting);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_resize.SetStateForComponent result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ omx_err = m_omx_encoder.SetStateForComponent(OMX_StateExecuting);
+ if (omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_encoder.SetStateForComponent result(0x%x)\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+
+ if(m_omx_encoder.BadState())
+ return false;
+ }
+ // on subsequent port settings changed event, we just copy the port settings
+ else
+ {
+ // a little surprising, make a note
+ CLog::Log(LOGDEBUG, "%s::%s m_omx_resize second port changed event\n", CLASSNAME, __func__);
+ m_omx_decoder.DisablePort(m_omx_decoder.GetOutputPort(), true);
+ m_omx_resize.DisablePort(m_omx_resize.GetInputPort(), true);
+
+ OMX_PARAM_PORTDEFINITIONTYPE port_def;
+ OMX_INIT_STRUCTURE(port_def);
+
+ port_def.nPortIndex = m_omx_decoder.GetOutputPort();
+ m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &port_def);
+ port_def.nPortIndex = m_omx_resize.GetInputPort();
+ m_omx_resize.SetParameter(OMX_IndexParamPortDefinition, &port_def);
+
+ omx_err = m_omx_resize.WaitForEvent(OMX_EventPortSettingsChanged);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s m_omx_resize.WaitForEvent=%x\n", CLASSNAME, __func__, omx_err);
+ return false;
+ }
+ m_omx_decoder.EnablePort(m_omx_decoder.GetOutputPort(), true);
+ m_omx_resize.EnablePort(m_omx_resize.GetInputPort(), true);
+ }
+ return true;
+}
+
+bool COMXImageReEnc::ReEncode(COMXImageFile &srcFile, unsigned int maxWidth, unsigned int maxHeight, void * &pDestBuffer, unsigned int &nDestSize)
+{
+ CSingleLock lock(m_OMXSection);
+ OMX_ERRORTYPE omx_err = OMX_ErrorNone;
+
+ COMXImage::ClampLimits(maxWidth, maxHeight, srcFile.GetWidth(), srcFile.GetHeight(), srcFile.GetOrientation() & 4);
+ unsigned int demuxer_bytes = srcFile.GetImageSize();
+ unsigned char *demuxer_content = (unsigned char *)srcFile.GetImageBuffer();
+ // initial dest buffer size
+ nDestSize = 0;
+
+ if(!demuxer_content || !demuxer_bytes)
+ {
+ CLog::Log(LOGERROR, "%s::%s %s no input buffer\n", CLASSNAME, __func__, srcFile.GetFilename());
+ return false;
+ }
+
+ if(!m_omx_decoder.Initialize("OMX.broadcom.image_decode", OMX_IndexParamImageInit))
+ {
+ CLog::Log(LOGERROR, "%s::%s %s error m_omx_decoder.Initialize\n", CLASSNAME, __func__, srcFile.GetFilename());
+ return false;
+ }
+
+ // set input format
+ OMX_PARAM_PORTDEFINITIONTYPE portParam;
+ OMX_INIT_STRUCTURE(portParam);
+ portParam.nPortIndex = m_omx_decoder.GetInputPort();
+
+ omx_err = m_omx_decoder.GetParameter(OMX_IndexParamPortDefinition, &portParam);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s %s error GetParameter:OMX_IndexParamPortDefinition omx_err(0x%08x)\n", CLASSNAME, __func__, srcFile.GetFilename(), omx_err);
+ return false;
+ }
+
+ portParam.nBufferCountActual = portParam.nBufferCountMin;
+ portParam.nBufferSize = std::max(portParam.nBufferSize, ALIGN_UP(demuxer_bytes, portParam.nBufferAlignment));
+ portParam.format.image.eCompressionFormat = OMX_IMAGE_CodingJPEG;
+
+ omx_err = m_omx_decoder.SetParameter(OMX_IndexParamPortDefinition, &portParam);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s %s error SetParameter:OMX_IndexParamPortDefinition omx_err(0x%08x)\n", CLASSNAME, __func__, srcFile.GetFilename(), omx_err);
+ return false;
+ }
+
+ omx_err = m_omx_decoder.AllocInputBuffers();
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s %s m_omx_decoder.AllocInputBuffers result(0x%x)", CLASSNAME, __func__, srcFile.GetFilename(), omx_err);
+ return false;
+ }
+
+ omx_err = m_omx_decoder.SetStateForComponent(OMX_StateExecuting);
+ if (omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s %s m_omx_decoder.SetStateForComponent result(0x%x)\n", CLASSNAME, __func__, srcFile.GetFilename(), omx_err);
+ return false;
+ }
+
+ bool port_settings_changed = false, eos = false;
+ while(demuxer_bytes > 0 || !port_settings_changed || !eos)
+ {
+ long timeout = 0;
+ if (demuxer_bytes)
+ {
+ OMX_BUFFERHEADERTYPE *omx_buffer = m_omx_decoder.GetInputBuffer(1000);
+ if(omx_buffer)
+ {
+ omx_buffer->nOffset = omx_buffer->nFlags = 0;
+
+ omx_buffer->nFilledLen = (demuxer_bytes > omx_buffer->nAllocLen) ? omx_buffer->nAllocLen : demuxer_bytes;
+ memcpy(omx_buffer->pBuffer, demuxer_content, omx_buffer->nFilledLen);
+
+ demuxer_content += omx_buffer->nFilledLen;
+ demuxer_bytes -= omx_buffer->nFilledLen;
+ if(demuxer_bytes == 0)
+ omx_buffer->nFlags |= OMX_BUFFERFLAG_EOS;
+
+ omx_err = m_omx_decoder.EmptyThisBuffer(omx_buffer);
+ if (omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s %s OMX_EmptyThisBuffer() failed with result(0x%x)\n", CLASSNAME, __func__, srcFile.GetFilename(), omx_err);
+ return false;
+ }
+ }
+ }
+ if (!demuxer_bytes)
+ {
+ // we've submitted all buffers so can wait now
+ timeout = 1000;
+ }
+
+ omx_err = m_omx_decoder.WaitForEvent(OMX_EventPortSettingsChanged, timeout);
+ if(omx_err == OMX_ErrorNone)
+ {
+ if (!HandlePortSettingChange(maxWidth, maxHeight, port_settings_changed))
+ {
+ CLog::Log(LOGERROR, "%s::%s %s HandlePortSettingChange() failed\n", srcFile.GetFilename(), CLASSNAME, __func__);
+ return false;
+ }
+ port_settings_changed = true;
+ }
+ else if(omx_err == OMX_ErrorStreamCorrupt)
+ {
+ CLog::Log(LOGERROR, "%s::%s %s - image not supported", CLASSNAME, __func__, srcFile.GetFilename());
+ return false;
+ }
+ else if(timeout || omx_err != OMX_ErrorTimeout)
+ {
+ CLog::Log(LOGERROR, "%s::%s %s WaitForEvent:OMX_EventPortSettingsChanged failed (%x)\n", CLASSNAME, __func__, srcFile.GetFilename(), omx_err);
+ return false;
+ }
+
+ if (!m_encoded_buffer && port_settings_changed && demuxer_bytes == 0)
+ {
+ m_encoded_buffer = m_omx_encoder.GetOutputBuffer();
+ omx_err = m_omx_encoder.FillThisBuffer(m_encoded_buffer);
+ if(omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s %s FillThisBuffer() failed (%x)\n", CLASSNAME, __func__, srcFile.GetFilename(), omx_err);
+ return false;
+ }
+ }
+ if (m_encoded_buffer)
+ {
+ omx_err = m_omx_encoder.WaitForOutputDone(1000);
+ if (omx_err != OMX_ErrorNone)
+ {
+ CLog::Log(LOGERROR, "%s::%s %s m_omx_encoder.WaitForOutputDone result(0x%x)\n", CLASSNAME, __func__, srcFile.GetFilename(), omx_err);
+ return false;
+ }
+ if (!m_encoded_buffer->nFilledLen)
+ {
+ CLog::Log(LOGERROR, "%s::%s %s m_omx_encoder.WaitForOutputDone no data\n", CLASSNAME, __func__, srcFile.GetFilename());
+ return false;
+ }
+ if (m_encoded_buffer->nFlags & OMX_BUFFERFLAG_EOS)
+ eos = true;
+
+ if (nDestSize + m_encoded_buffer->nFilledLen > m_nDestAllocSize)
+ {
+ m_nDestAllocSize = std::max(1024U*1024U, m_nDestAllocSize*2);
+ m_pDestBuffer = realloc(m_pDestBuffer, m_nDestAllocSize);
+ }
+ memcpy((char *)m_pDestBuffer + nDestSize, m_encoded_buffer->pBuffer, m_encoded_buffer->nFilledLen);
+ nDestSize += m_encoded_buffer->nFilledLen;
+ m_encoded_buffer = NULL;
+ }
+ }
+
+ Close();
+
+ if(m_omx_decoder.BadState())
+ return false;
+
+ pDestBuffer = m_pDestBuffer;
+ CLog::Log(LOGDEBUG, "%s::%s : %s %dx%d -> %dx%d\n", CLASSNAME, __func__, srcFile.GetFilename(), srcFile.GetWidth(), srcFile.GetHeight(), maxWidth, maxHeight);
+
+ return true;
+}
static bool CreateThumbnailFromSurface(unsigned char* buffer, unsigned int width, unsigned int height,
unsigned int format, unsigned int pitch, const CStdString& destFile);
static bool ClampLimits(unsigned int &width, unsigned int &height, unsigned int m_width, unsigned int m_height, bool transposed = false);
+ static bool CreateThumb(const CStdString& srcFile, unsigned int width, unsigned int height, std::string &additional_info, const CStdString& destFile);
};
class COMXImageFile
CCriticalSection m_OMXSection;
};
+class COMXImageReEnc
+{
+public:
+ COMXImageReEnc();
+ virtual ~COMXImageReEnc();
+
+ // Required overrides
+ void Close();
+ bool ReEncode(COMXImageFile &srcFile, unsigned int width, unsigned int height, void * &pDestBuffer, unsigned int &nDestSize);
+protected:
+ bool HandlePortSettingChange(unsigned int resize_width, unsigned int resize_height, bool port_settings_changed);
+ // Components
+ COMXCoreComponent m_omx_decoder;
+ COMXCoreComponent m_omx_resize;
+ COMXCoreComponent m_omx_encoder;
+ COMXCoreTunel m_omx_tunnel_decode;
+ COMXCoreTunel m_omx_tunnel_resize;
+ OMX_BUFFERHEADERTYPE *m_encoded_buffer;
+ CCriticalSection m_OMXSection;
+ void *m_pDestBuffer;
+ unsigned int m_nDestAllocSize;
+};
+
#endif
/* GetDataFormat is setting up evrything */
m_format.m_dataFormat = GetDataFormat(m_hints);
- m_format.m_channelLayout.Reset();
+ uint64_t channelMap = 0;
if (m_pAudioCodec && !m_passthrough)
- m_format.m_channelLayout = m_pAudioCodec->GetChannelMap();
+ channelMap = m_pAudioCodec->GetChannelMap();
else if (m_passthrough)
- {
// we just want to get the channel count right to stop OMXAudio.cpp rejecting stream
// the actual layout is not used
- if (m_nChannels > 0 ) m_format.m_channelLayout += AE_CH_FL;
- if (m_nChannels > 1 ) m_format.m_channelLayout += AE_CH_FR;
- if (m_nChannels > 2 ) m_format.m_channelLayout += AE_CH_FC;
- if (m_nChannels > 3 ) m_format.m_channelLayout += AE_CH_LFE;
- if (m_nChannels > 4 ) m_format.m_channelLayout += AE_CH_BL;
- if (m_nChannels > 5 ) m_format.m_channelLayout += AE_CH_BR;
- }
- bool bAudioRenderOpen = m_omxAudio.Initialize(m_format, m_av_clock, m_hints, m_passthrough, m_hw_decode);
+ channelMap = (1<<m_nChannels)-1;
+ bool bAudioRenderOpen = m_omxAudio.Initialize(m_format, m_av_clock, m_hints, channelMap, m_passthrough, m_hw_decode);
m_codec_name = "";
m_bad_state = !bAudioRenderOpen;
--- /dev/null
+/*
+ * Copyright (C) 2005-2010 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program 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, or (at your option)
+ * any later version.
+ *
+ * This Program 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 XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+
+#include <cstdlib>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+//#include "MathUtils.h"
+#include "PCMRemap.h"
+#include "utils/log.h"
+#include "settings/Settings.h"
+#include "settings/AdvancedSettings.h"
+#ifdef _WIN32
+#include "../win32/PlatformDefs.h"
+#endif
+
+static enum PCMChannels PCMLayoutMap[PCM_MAX_LAYOUT][PCM_MAX_CH + 1] =
+{
+ /* 2.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_INVALID},
+ /* 2.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_LOW_FREQUENCY, PCM_INVALID},
+ /* 3.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_INVALID},
+ /* 3.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_LOW_FREQUENCY, PCM_INVALID},
+ /* 4.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_INVALID},
+ /* 4.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_LOW_FREQUENCY, PCM_INVALID},
+ /* 5.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_INVALID},
+ /* 5.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_LOW_FREQUENCY, PCM_INVALID},
+ /* 7.0 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_SIDE_LEFT, PCM_SIDE_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_INVALID},
+ /* 7.1 */ {PCM_FRONT_LEFT, PCM_FRONT_RIGHT, PCM_FRONT_CENTER, PCM_SIDE_LEFT, PCM_SIDE_RIGHT, PCM_BACK_LEFT, PCM_BACK_RIGHT, PCM_LOW_FREQUENCY, PCM_INVALID}
+};
+
+/*
+ map missing output into channel @ volume level
+ the order of this table is important, mix tables can not depend on channels that have not been defined yet
+ eg, FC can only be mixed into FL, FR as they are the only channels that have been defined
+*/
+#define PCM_MAX_MIX 3
+static struct PCMMapInfo PCMDownmixTable[PCM_MAX_CH][PCM_MAX_MIX] =
+{
+ /* PCM_FRONT_LEFT */
+ {
+ {PCM_INVALID}
+ },
+ /* PCM_FRONT_RIGHT */
+ {
+ {PCM_INVALID}
+ },
+ /* PCM_FRONT_CENTER */
+ {
+ {PCM_FRONT_LEFT_OF_CENTER , 1.0},
+ {PCM_FRONT_RIGHT_OF_CENTER, 1.0},
+ {PCM_INVALID}
+ },
+ /* PCM_LOW_FREQUENCY */
+ {
+ /*
+ A/52B 7.8 paragraph 2 recomends +10db
+ but due to horrible clipping when normalize
+ is disabled we set this to 1.0
+ */
+ {PCM_FRONT_LEFT , 1.0},//3.5},
+ {PCM_FRONT_RIGHT , 1.0},//3.5},
+ {PCM_INVALID}
+ },
+ /* PCM_BACK_LEFT */
+ {
+ {PCM_FRONT_LEFT , 1.0},
+ {PCM_INVALID}
+ },
+ /* PCM_BACK_RIGHT */
+ {
+ {PCM_FRONT_RIGHT , 1.0},
+ {PCM_INVALID}
+ },
+ /* PCM_FRONT_LEFT_OF_CENTER */
+ {
+ {PCM_FRONT_LEFT , 1.0},
+ {PCM_FRONT_CENTER , 1.0, true},
+ {PCM_INVALID}
+ },
+ /* PCM_FRONT_RIGHT_OF_CENTER */
+ {
+ {PCM_FRONT_RIGHT , 1.0},
+ {PCM_FRONT_CENTER , 1.0, true},
+ {PCM_INVALID}
+ },
+ /* PCM_BACK_CENTER */
+ {
+ {PCM_BACK_LEFT , 1.0},
+ {PCM_BACK_RIGHT , 1.0},
+ {PCM_INVALID}
+ },
+ /* PCM_SIDE_LEFT */
+ {
+ {PCM_FRONT_LEFT , 1.0},
+ {PCM_BACK_LEFT , 1.0},
+ {PCM_INVALID}
+ },
+ /* PCM_SIDE_RIGHT */
+ {
+ {PCM_FRONT_RIGHT , 1.0},
+ {PCM_BACK_RIGHT , 1.0},
+ {PCM_INVALID}
+ },
+ /* PCM_TOP_FRONT_LEFT */
+ {
+ {PCM_FRONT_LEFT , 1.0},
+ {PCM_INVALID}
+ },
+ /* PCM_TOP_FRONT_RIGHT */
+ {
+ {PCM_FRONT_RIGHT , 1.0},
+ {PCM_INVALID}
+ },
+ /* PCM_TOP_FRONT_CENTER */
+ {
+ {PCM_TOP_FRONT_LEFT , 1.0},
+ {PCM_TOP_FRONT_RIGHT , 1.0},
+ {PCM_INVALID}
+ },
+ /* PCM_TOP_CENTER */
+ {
+ {PCM_TOP_FRONT_LEFT , 1.0},
+ {PCM_TOP_FRONT_RIGHT , 1.0},
+ {PCM_INVALID}
+ },
+ /* PCM_TOP_BACK_LEFT */
+ {
+ {PCM_BACK_LEFT , 1.0},
+ {PCM_INVALID}
+ },
+ /* PCM_TOP_BACK_RIGHT */
+ {
+ {PCM_BACK_RIGHT , 1.0},
+ {PCM_INVALID}
+ },
+ /* PCM_TOP_BACK_CENTER */
+ {
+ {PCM_TOP_BACK_LEFT , 1.0},
+ {PCM_TOP_BACK_RIGHT , 1.0},
+ {PCM_INVALID}
+ }
+};
+
+CPCMRemap::CPCMRemap() :
+ m_inSet (false),
+ m_outSet (false),
+ m_inChannels (0),
+ m_outChannels (0),
+ m_inSampleSize(0),
+ m_ignoreLayout(false),
+ m_buf(NULL),
+ m_bufsize(0),
+ m_attenuation (1.0),
+ m_attenuationInc(0.0),
+ m_attenuationMin(1.0),
+ m_sampleRate (48000.0), //safe default
+ m_holdCounter (0),
+ m_limiterEnabled(false)
+{
+ Dispose();
+}
+
+CPCMRemap::~CPCMRemap()
+{
+ Dispose();
+}
+
+void CPCMRemap::Dispose()
+{
+ free(m_buf);
+ m_buf = NULL;
+ m_bufsize = 0;
+}
+
+/* resolves the channels recursively and returns the new index of tablePtr */
+struct PCMMapInfo* CPCMRemap::ResolveChannel(enum PCMChannels channel, float level, bool ifExists, std::vector<enum PCMChannels> path, struct PCMMapInfo *tablePtr)
+{
+ if (channel == PCM_INVALID) return tablePtr;
+
+ /* if its a 1 to 1 mapping, return */
+ if (m_useable[channel])
+ {
+ tablePtr->channel = channel;
+ tablePtr->level = level;
+
+ ++tablePtr;
+ tablePtr->channel = PCM_INVALID;
+ return tablePtr;
+ } else
+ if (ifExists)
+ level /= 2;
+
+ struct PCMMapInfo *info;
+ std::vector<enum PCMChannels>::iterator itt;
+
+ for(info = PCMDownmixTable[channel]; info->channel != PCM_INVALID; ++info)
+ {
+ /* make sure we are not about to recurse into ourself */
+ bool found = false;
+ for(itt = path.begin(); itt != path.end(); ++itt)
+ if (*itt == info->channel)
+ {
+ found = true;
+ break;
+ }
+
+ if (found)
+ continue;
+
+ path.push_back(channel);
+ float l = (info->level * (level / 100)) * 100;
+ tablePtr = ResolveChannel(info->channel, l, info->ifExists, path, tablePtr);
+ path.pop_back();
+ }
+
+ return tablePtr;
+}
+
+/*
+ Builds a lookup table without extra adjustments, useful if we simply
+ want to find out which channels are active.
+ For final adjustments, BuildMap() is used.
+*/
+void CPCMRemap::ResolveChannels()
+{
+ unsigned int in_ch, out_ch;
+ bool hasSide = false;
+ bool hasBack = false;
+
+ memset(m_useable, 0, sizeof(m_useable));
+
+ if (!m_outSet)
+ {
+ /* Output format is not known yet, assume the full configured map.
+ * Note that m_ignoreLayout-using callers normally ignore the result of
+ * this function when !m_outSet, when it is called only for an advice for
+ * the caller of SetInputFormat about the best possible output map, and
+ * they can still set their output format arbitrarily in their call to
+ * SetOutputFormat. */
+ for (enum PCMChannels *chan = PCMLayoutMap[m_channelLayout]; *chan != PCM_INVALID; ++chan)
+ m_useable[*chan] = true;
+ }
+ else if (m_ignoreLayout)
+ {
+ for(out_ch = 0; out_ch < m_outChannels; ++out_ch)
+ m_useable[m_outMap[out_ch]] = true;
+ }
+ else
+ {
+ /* figure out what channels we have and can use */
+ for(enum PCMChannels *chan = PCMLayoutMap[m_channelLayout]; *chan != PCM_INVALID; ++chan)
+ {
+ for(out_ch = 0; out_ch < m_outChannels; ++out_ch)
+ if (m_outMap[out_ch] == *chan)
+ {
+ m_useable[*chan] = true;
+ break;
+ }
+ }
+ }
+
+ /* force mono audio to front left and front right */
+ if (!m_ignoreLayout && m_inChannels == 1 && m_inMap[0] == PCM_FRONT_CENTER
+ && m_useable[PCM_FRONT_LEFT] && m_useable[PCM_FRONT_RIGHT])
+ {
+ CLog::Log(LOGDEBUG, "CPCMRemap: Mapping mono audio to front left and front right");
+ m_useable[PCM_FRONT_CENTER] = false;
+ m_useable[PCM_FRONT_LEFT_OF_CENTER] = false;
+ m_useable[PCM_FRONT_RIGHT_OF_CENTER] = false;
+ }
+
+ /* see if our input has side/back channels */
+ for(in_ch = 0; in_ch < m_inChannels; ++in_ch)
+ switch(m_inMap[in_ch])
+ {
+ case PCM_SIDE_LEFT:
+ case PCM_SIDE_RIGHT:
+ hasSide = true;
+ break;
+
+ case PCM_BACK_LEFT:
+ case PCM_BACK_RIGHT:
+ hasBack = true;
+ break;
+
+ default:;
+ }
+
+ /* if our input has side, and not back channels, and our output doesnt have side channels */
+ if (hasSide && !hasBack && (!m_useable[PCM_SIDE_LEFT] || !m_useable[PCM_SIDE_RIGHT]))
+ {
+ CLog::Log(LOGDEBUG, "CPCMRemap: Forcing side channel map to back channels");
+ for(in_ch = 0; in_ch < m_inChannels; ++in_ch)
+ if (m_inMap[in_ch] == PCM_SIDE_LEFT ) m_inMap[in_ch] = PCM_BACK_LEFT;
+ else if (m_inMap[in_ch] == PCM_SIDE_RIGHT) m_inMap[in_ch] = PCM_BACK_RIGHT;
+ }
+
+ /* resolve all the channels */
+ struct PCMMapInfo table[PCM_MAX_CH + 1], *info, *dst;
+ std::vector<enum PCMChannels> path;
+
+ for (int i = 0; i < PCM_MAX_CH + 1; i++)
+ {
+ for (int j = 0; j < PCM_MAX_CH + 1; j++)
+ m_lookupMap[i][j].channel = PCM_INVALID;
+ }
+
+ memset(m_counts, 0, sizeof(m_counts));
+ for(in_ch = 0; in_ch < m_inChannels; ++in_ch) {
+
+ for (int i = 0; i < PCM_MAX_CH + 1; i++)
+ table[i].channel = PCM_INVALID;
+
+ ResolveChannel(m_inMap[in_ch], 1.0f, false, path, table);
+ for(info = table; info->channel != PCM_INVALID; ++info)
+ {
+ /* find the end of the table */
+ for(dst = m_lookupMap[info->channel]; dst->channel != PCM_INVALID; ++dst);
+
+ /* append it to the table and set its input offset */
+ dst->channel = m_inMap[in_ch];
+ dst->in_offset = in_ch * 2;
+ dst->level = info->level;
+ m_counts[dst->channel]++;
+ }
+ }
+}
+
+/*
+ builds a lookup table to convert from the input mapping to the output
+ mapping, this decreases the amount of work per sample to remap it.
+*/
+void CPCMRemap::BuildMap()
+{
+ struct PCMMapInfo *dst;
+ unsigned int out_ch;
+
+ if (!m_inSet || !m_outSet) return;
+
+ m_inStride = m_inSampleSize * m_inChannels ;
+ m_outStride = m_inSampleSize * m_outChannels;
+
+ /* see if we need to normalize the levels */
+ bool dontnormalize = CSettings::Get().GetBool("audiooutput.normalizelevels");
+ CLog::Log(LOGDEBUG, "CPCMRemap: Downmix normalization is %s", (dontnormalize ? "disabled" : "enabled"));
+
+ ResolveChannels();
+
+ /* convert the levels into RMS values */
+ float loudest = 0.0;
+ bool hasLoudest = false;
+
+ for(out_ch = 0; out_ch < m_outChannels; ++out_ch)
+ {
+ float scale = 0;
+ int count = 0;
+ for(dst = m_lookupMap[m_outMap[out_ch]]; dst->channel != PCM_INVALID; ++dst)
+ {
+ dst->copy = false;
+ dst->level = dst->level / sqrt((float)m_counts[dst->channel]);
+ scale += dst->level;
+ ++count;
+ }
+
+ /* if there is only 1 channel to mix, and the level is 1.0, then just copy the channel */
+ dst = m_lookupMap[m_outMap[out_ch]];
+ if (count == 1 && dst->level > 0.99 && dst->level < 1.01)
+ dst->copy = true;
+
+ /* normalize the levels if it is turned on */
+ if (!dontnormalize)
+ for(dst = m_lookupMap[m_outMap[out_ch]]; dst->channel != PCM_INVALID; ++dst)
+ {
+ dst->level /= scale;
+ /* find the loudest output level we have that is not 1-1 */
+ if (dst->level < 1.0 && loudest < dst->level)
+ {
+ loudest = dst->level;
+ hasLoudest = true;
+ }
+ }
+ }
+
+ /* adjust the channels that are too loud */
+ for(out_ch = 0; out_ch < m_outChannels; ++out_ch)
+ {
+ CStdString s = "", f;
+ for(dst = m_lookupMap[m_outMap[out_ch]]; dst->channel != PCM_INVALID; ++dst)
+ {
+ if (hasLoudest && dst->copy)
+ {
+ dst->level = loudest;
+ dst->copy = false;
+ }
+
+ f.Format("%s(%f%s) ", PCMChannelStr(dst->channel).c_str(), dst->level, dst->copy ? "*" : "");
+ s += f;
+ }
+ CLog::Log(LOGDEBUG, "CPCMRemap: %s = %s\n", PCMChannelStr(m_outMap[out_ch]).c_str(), s.c_str());
+ }
+}
+
+void CPCMRemap::DumpMap(CStdString info, unsigned int channels, enum PCMChannels *channelMap)
+{
+ if (channelMap == NULL)
+ {
+ CLog::Log(LOGINFO, "CPCMRemap: %s channel map: NULL", info.c_str());
+ return;
+ }
+
+ CStdString mapping;
+ for(unsigned int i = 0; i < channels; ++i)
+ mapping += ((i == 0) ? "" : ",") + PCMChannelStr(channelMap[i]);
+
+ CLog::Log(LOGINFO, "CPCMRemap: %s channel map: %s\n", info.c_str(), mapping.c_str());
+}
+
+void CPCMRemap::Reset()
+{
+ m_inSet = false;
+ m_outSet = false;
+ Dispose();
+}
+
+/* sets the input format, and returns the requested channel layout */
+enum PCMChannels *CPCMRemap::SetInputFormat(unsigned int channels, enum PCMChannels *channelMap, unsigned int sampleSize, unsigned int sampleRate)
+{
+ m_inChannels = channels;
+ m_inSampleSize = sampleSize;
+ m_sampleRate = sampleRate;
+ m_inSet = channelMap != NULL;
+ if (channelMap)
+ memcpy(m_inMap, channelMap, sizeof(enum PCMChannels) * channels);
+
+ /* get the audio layout, and count the channels in it */
+ m_channelLayout = (enum PCMLayout)std::max(0, CSettings::Get().GetInt("audiooutput.channels")-1);
+ if (m_channelLayout >= PCM_MAX_LAYOUT) m_channelLayout = PCM_LAYOUT_2_0;
+
+
+ DumpMap("I", channels, channelMap);
+ BuildMap();
+
+ /* now remove the empty channels from PCMLayoutMap;
+ * we don't perform upmixing so we want the minimum amount of those */
+ if (channelMap) {
+ if (!m_outSet)
+ ResolveChannels(); /* Do basic channel resolving to find out the empty channels;
+ * If m_outSet == true, this was done already by BuildMap() above */
+ int i = 0;
+ for (enum PCMChannels *chan = PCMLayoutMap[m_channelLayout]; *chan != PCM_INVALID; ++chan)
+ if (m_lookupMap[*chan][0].channel != PCM_INVALID) {
+ /* something is mapped here, so add the channel */
+ m_layoutMap[i++] = *chan;
+ }
+ m_layoutMap[i] = PCM_INVALID;
+ } else
+ memcpy(m_layoutMap, PCMLayoutMap[m_channelLayout], sizeof(PCMLayoutMap[m_channelLayout]));
+
+ m_attenuation = 1.0;
+ m_attenuationInc = 1.0;
+ m_holdCounter = 0;
+
+ return m_layoutMap;
+}
+
+/* sets the output format supported by the audio renderer */
+void CPCMRemap::SetOutputFormat(unsigned int channels, enum PCMChannels *channelMap, bool ignoreLayout/* = false */)
+{
+ m_outChannels = channels;
+ m_outSet = channelMap != NULL;
+ m_ignoreLayout = ignoreLayout;
+ if (channelMap)
+ memcpy(m_outMap, channelMap, sizeof(enum PCMChannels) * channels);
+
+ DumpMap("O", channels, channelMap);
+ BuildMap();
+
+ m_attenuation = 1.0;
+ m_attenuationInc = 1.0;
+ m_holdCounter = 0;
+}
+
+#if 0
+void CPCMRemap::Remap(void *data, void *out, unsigned int samples, long drc)
+{
+ float gain = 1.0f;
+ if (drc > 0)
+ gain = pow(10.0f, (float)drc / 2000.0f);
+
+ Remap(data, out, samples, gain);
+}
+
+/* remap the supplied data into out, which must be pre-allocated */
+void CPCMRemap::Remap(void *data, void *out, unsigned int samples, float gain /*= 1.0f*/)
+{
+ CheckBufferSize(samples * m_outChannels * sizeof(float));
+
+ //set output buffer to 0
+ memset(out, 0, samples * m_outChannels * m_inSampleSize);
+
+ //set intermediate buffer to 0
+ memset(m_buf, 0, m_bufsize);
+
+ ProcessInput(data, out, samples, gain);
+ AddGain(m_buf, samples * m_outChannels, gain);
+ ProcessLimiter(samples, gain);
+ ProcessOutput(out, samples, gain);
+}
+
+void CPCMRemap::CheckBufferSize(int size)
+{
+ if (m_bufsize < size)
+ {
+ m_bufsize = size;
+ m_buf = (float*)realloc(m_buf, m_bufsize);
+ }
+}
+
+void CPCMRemap::ProcessInput(void* data, void* out, unsigned int samples, float gain)
+{
+ for (unsigned int ch = 0; ch < m_outChannels; ch++)
+ {
+ struct PCMMapInfo *info = m_lookupMap[m_outMap[ch]];
+ if (info->channel == PCM_INVALID)
+ continue;
+
+ if (info->copy && gain == 1.0f) //do direct copy
+ {
+ uint8_t* src = (uint8_t*)data + info->in_offset;
+ uint8_t* dst = (uint8_t*)out + ch * m_inSampleSize;
+ uint8_t* dstend = dst + samples * m_outStride;
+ while (dst != dstend)
+ {
+ *(int16_t*)dst = *(int16_t*)src;
+ src += m_inStride;
+ dst += m_outStride;
+ }
+ }
+ else //needs some volume change or mixing, put into intermediate buffer
+ {
+ for(; info->channel != PCM_INVALID; info++)
+ {
+ uint8_t* src = (uint8_t*)data + info->in_offset;
+ float* dst = m_buf + ch;
+ float* dstend = dst + samples * m_outChannels;
+ while (dst != dstend)
+ {
+ *dst += (float)(*(int16_t*)src) * info->level;
+ src += m_inStride;
+ dst += m_outChannels;
+ }
+ }
+ }
+ }
+}
+
+void CPCMRemap::AddGain(float* buf, unsigned int samples, float gain)
+{
+ if (gain != 1.0f) //needs a gain change
+ {
+ float* ptr = m_buf;
+ float* end = m_buf + samples;
+ while (ptr != end)
+ *(ptr++) *= gain;
+ }
+}
+
+void CPCMRemap::ProcessLimiter(unsigned int samples, float gain)
+{
+ //check total gain for each output channel
+ float highestgain = 1.0f;
+ for (unsigned int ch = 0; ch < m_outChannels; ch++)
+ {
+ struct PCMMapInfo *info = m_lookupMap[m_outMap[ch]];
+ if (info->channel == PCM_INVALID)
+ continue;
+
+ float chgain = 0.0f;
+ for(; info->channel != PCM_INVALID; info++)
+ chgain += info->level * gain;
+
+ if (chgain > highestgain)
+ highestgain = chgain;
+ }
+
+ m_attenuationMin = 1.0f;
+
+ //if one of the channels can clip, enable a limiter
+ if (highestgain > 1.0001f)
+ {
+ m_attenuationMin = m_attenuation;
+
+ if (!m_limiterEnabled)
+ {
+ CLog::Log(LOGDEBUG, "CPCMRemap:: max gain: %f, enabling limiter", highestgain);
+ m_limiterEnabled = true;
+ }
+
+ for (unsigned int i = 0; i < samples; i++)
+ {
+ //for each collection of samples, get the highest absolute value
+ float maxAbs = 0.0f;
+ for (unsigned int outch = 0; outch < m_outChannels; outch++)
+ {
+ float absval = fabs(m_buf[i * m_outChannels + outch]) / 32768.0f;
+ if (maxAbs < absval)
+ maxAbs = absval;
+ }
+
+ //if attenuatedAbs is higher than 1.0f, audio is clipping
+ float attenuatedAbs = maxAbs * m_attenuation;
+ if (attenuatedAbs > 1.0f)
+ {
+ //set m_attenuation so that m_attenuation * sample is the maximum output value
+ m_attenuation = 1.0f / maxAbs;
+ if (m_attenuation < m_attenuationMin)
+ m_attenuationMin = m_attenuation;
+ //value to add to m_attenuation to make it 1.0f
+ m_attenuationInc = 1.0f - m_attenuation;
+ //amount of samples to hold m_attenuation
+ m_holdCounter = MathUtils::round_int(m_sampleRate * g_advancedSettings.m_limiterHold);
+ }
+ else if (m_attenuation < 1.0f && attenuatedAbs > 0.95f)
+ {
+ //if we're attenuating and we get within 5% of clipping, hold m_attenuation
+ m_attenuationInc = 1.0f - m_attenuation;
+ m_holdCounter = MathUtils::round_int(m_sampleRate * g_advancedSettings.m_limiterHold);
+ }
+
+ //apply attenuation
+ for (unsigned int outch = 0; outch < m_outChannels; outch++)
+ m_buf[i * m_outChannels + outch] *= m_attenuation;
+
+ if (m_holdCounter)
+ {
+ //hold m_attenuation
+ m_holdCounter--;
+ }
+ else if (m_attenuationInc > 0.0f)
+ {
+ //move m_attenuation to 1.0 in g_advancedSettings.m_limiterRelease seconds
+ m_attenuation += m_attenuationInc / m_sampleRate / g_advancedSettings.m_limiterRelease;
+ if (m_attenuation > 1.0f)
+ {
+ m_attenuation = 1.0f;
+ m_attenuationInc = 0.0f;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (m_limiterEnabled)
+ {
+ CLog::Log(LOGDEBUG, "CPCMRemap:: max gain: %f, disabling limiter", highestgain);
+ m_limiterEnabled = false;
+ }
+
+ //reset the limiter
+ m_attenuation = 1.0f;
+ m_attenuationInc = 0.0f;
+ m_holdCounter = 0;
+ }
+}
+
+void CPCMRemap::ProcessOutput(void* out, unsigned int samples, float gain)
+{
+ //copy from intermediate buffer to output
+ for (unsigned int ch = 0; ch < m_outChannels; ch++)
+ {
+ struct PCMMapInfo *info = m_lookupMap[m_outMap[ch]];
+ if (info->channel == PCM_INVALID)
+ continue;
+
+ if (!info->copy || gain != 1.0f)
+ {
+ float* src = m_buf + ch;
+ uint8_t* dst = (uint8_t*)out + ch * m_inSampleSize;
+ uint8_t* dstend = dst + samples * m_outStride;
+
+ while(dst != dstend)
+ {
+ *(int16_t*)dst = MathUtils::round_int(std::min(std::max(*src, (float)INT16_MIN), (float)INT16_MAX));
+ src += m_outChannels;
+ dst += m_outStride;
+ }
+ }
+ }
+}
+
+bool CPCMRemap::CanRemap()
+{
+ return (m_inSet && m_outSet);
+}
+
+int CPCMRemap::InBytesToFrames(int bytes)
+{
+ return bytes / m_inSampleSize / m_inChannels;
+}
+
+int CPCMRemap::FramesToOutBytes(int frames)
+{
+ return frames * m_inSampleSize * m_outChannels;
+}
+
+int CPCMRemap::FramesToInBytes(int frames)
+{
+ return frames * m_inSampleSize * m_inChannels;
+}
+#endif
+CStdString CPCMRemap::PCMChannelStr(enum PCMChannels ename)
+{
+ const char* PCMChannelName[] =
+ {
+ "FL",
+ "FR",
+ "CE",
+ "LFE",
+ "BL",
+ "BR",
+ "FLOC",
+ "FROC",
+ "BC",
+ "SL",
+ "SR",
+ "TFL",
+ "TFR",
+ "TFC",
+ "TC",
+ "TBL",
+ "TBR",
+ "TBC"
+ };
+
+ int namepos = (int)ename;
+ CStdString namestr;
+
+ if (namepos < 0 || namepos >= (int)(sizeof(PCMChannelName) / sizeof(const char*)))
+ namestr.Format("UNKNOWN CHANNEL:%i", namepos);
+ else
+ namestr = PCMChannelName[namepos];
+
+ return namestr;
+}
+#if 0
+CStdString CPCMRemap::PCMLayoutStr(enum PCMLayout ename)
+{
+ const char* PCMLayoutName[] =
+ {
+ "2.0",
+ "2.1",
+ "3.0",
+ "3.1",
+ "4.0",
+ "4.1",
+ "5.0",
+ "5.1",
+ "7.0",
+ "7.1"
+ };
+
+ int namepos = (int)ename;
+ CStdString namestr;
+
+ if (namepos < 0 || namepos >= (int)(sizeof(PCMLayoutName) / sizeof(const char*)))
+ namestr.Format("UNKNOWN LAYOUT:%i", namepos);
+ else
+ namestr = PCMLayoutName[namepos];
+
+ return namestr;
+}
+#endif
+
+
+void CPCMRemap::GetDownmixMatrix(float *downmix)
+{
+ for (int i=0; i<8*8; i++)
+ downmix[i] = 0.0f;
+
+ for (unsigned int ch = 0; ch < m_outChannels; ch++)
+ {
+ struct PCMMapInfo *info = m_lookupMap[m_outMap[ch]];
+ if (info->channel == PCM_INVALID)
+ continue;
+
+ for(; info->channel != PCM_INVALID; info++)
+ downmix[8*ch + (info->in_offset>>1)] = info->level;
+ }
+}
--- /dev/null
+#ifndef __PCM_REMAP__H__
+#define __PCM_REMAP__H__
+
+/*
+ * Copyright (C) 2005-2010 Team XBMC
+ * http://www.xbmc.org
+ *
+ * This Program 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, or (at your option)
+ * any later version.
+ *
+ * This Program 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 XBMC; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdint.h>
+#include <vector>
+#include "utils/StdString.h"
+
+#define PCM_MAX_CH 18
+enum PCMChannels
+{
+ PCM_INVALID = -1,
+ PCM_FRONT_LEFT,
+ PCM_FRONT_RIGHT,
+ PCM_FRONT_CENTER,
+ PCM_LOW_FREQUENCY,
+ PCM_BACK_LEFT,
+ PCM_BACK_RIGHT,
+ PCM_FRONT_LEFT_OF_CENTER,
+ PCM_FRONT_RIGHT_OF_CENTER,
+ PCM_BACK_CENTER,
+ PCM_SIDE_LEFT,
+ PCM_SIDE_RIGHT,
+ PCM_TOP_FRONT_LEFT,
+ PCM_TOP_FRONT_RIGHT,
+ PCM_TOP_FRONT_CENTER,
+ PCM_TOP_CENTER,
+ PCM_TOP_BACK_LEFT,
+ PCM_TOP_BACK_RIGHT,
+ PCM_TOP_BACK_CENTER
+};
+
+#define PCM_MAX_LAYOUT 10
+enum PCMLayout
+{
+ PCM_LAYOUT_2_0 = 0,
+ PCM_LAYOUT_2_1,
+ PCM_LAYOUT_3_0,
+ PCM_LAYOUT_3_1,
+ PCM_LAYOUT_4_0,
+ PCM_LAYOUT_4_1,
+ PCM_LAYOUT_5_0,
+ PCM_LAYOUT_5_1,
+ PCM_LAYOUT_7_0,
+ PCM_LAYOUT_7_1
+};
+
+struct PCMMapInfo
+{
+ enum PCMChannels channel;
+ float level;
+ bool ifExists;
+ int in_offset;
+ bool copy;
+};
+
+//! Channels remapper class
+/*!
+ The usual set-up process:
+ - user calls SetInputFormat with the input channels information
+ - SetInputFormat responds with a channelmap corresponding to the speaker
+ layout that the user has configured, with empty (according to information
+ calculated from the input channelmap) channels removed
+ - user uses this information to create the desired output channelmap,
+ and calls SetOutputFormat to set it (if the channelmap contains channels
+ that do not exist in the configured speaker layout, they will contain
+ only silence unless ignoreLayout is true)
+ */
+
+class CPCMRemap
+{
+protected:
+ bool m_inSet, m_outSet;
+ enum PCMLayout m_channelLayout;
+ unsigned int m_inChannels, m_outChannels;
+ unsigned int m_inSampleSize;
+ enum PCMChannels m_inMap [PCM_MAX_CH];
+ enum PCMChannels m_outMap[PCM_MAX_CH];
+ enum PCMChannels m_layoutMap[PCM_MAX_CH + 1];
+
+ bool m_ignoreLayout;
+ bool m_useable [PCM_MAX_CH];
+ int m_inStride, m_outStride;
+ struct PCMMapInfo m_lookupMap[PCM_MAX_CH + 1][PCM_MAX_CH + 1];
+ int m_counts[PCM_MAX_CH];
+
+ float* m_buf;
+ int m_bufsize;
+ float m_attenuation;
+ float m_attenuationInc;
+ float m_attenuationMin; //lowest attenuation value during a call of Remap(), used for the codec info
+ float m_sampleRate;
+ unsigned int m_holdCounter;
+ bool m_limiterEnabled;
+ bool m_dontnormalize;
+
+ struct PCMMapInfo* ResolveChannel(enum PCMChannels channel, float level, bool ifExists, std::vector<enum PCMChannels> path, struct PCMMapInfo *tablePtr);
+ void ResolveChannels(); //!< Partial BuildMap(), just enough to see which output channels are active
+ void BuildMap();
+ void DumpMap(CStdString info, int unsigned channels, enum PCMChannels *channelMap);
+ void Dispose();
+ CStdString PCMChannelStr(enum PCMChannels ename);
+ CStdString PCMLayoutStr(enum PCMLayout ename);
+
+ void CheckBufferSize(int size);
+ void ProcessInput(void* data, void* out, unsigned int samples, float gain);
+ void AddGain(float* buf, unsigned int samples, float gain);
+ void ProcessLimiter(unsigned int samples, float gain);
+ void ProcessOutput(void* out, unsigned int samples, float gain);
+
+public:
+
+ CPCMRemap();
+ ~CPCMRemap();
+
+ void Reset();
+ enum PCMChannels *SetInputFormat (unsigned int channels, enum PCMChannels *channelMap, unsigned int sampleSize, unsigned int sampleRate);
+ void SetOutputFormat(unsigned int channels, enum PCMChannels *channelMap, bool ignoreLayout = false);
+#if 0
+ void Remap(void *data, void *out, unsigned int samples, long drc);
+ void Remap(void *data, void *out, unsigned int samples, float gain = 1.0f);
+ bool CanRemap();
+ int InBytesToFrames (int bytes );
+ int FramesToOutBytes(int frames);
+ int FramesToInBytes (int frames);
+#endif
+ float GetCurrentAttenuation() { return m_attenuationMin; }
+ void GetDownmixMatrix(float *downmix);
+};
+
+#endif
void CEpgInfoTag::Serialize(CVariant &value) const
{
+ value["broadcastid"] = m_iUniqueBroadcastID;
value["rating"] = m_iStarRating;
value["title"] = m_strTitle;
value["plotoutline"] = m_strPlotOutline;
value["endtime"] = m_endTime.IsValid() ? m_endTime.GetAsDBDateTime() : StringUtils::EmptyString;
value["runtime"] = StringUtils::Format("%d", GetDuration() / 60);
value["firstaired"] = m_firstAired.IsValid() ? m_firstAired.GetAsDBDate() : StringUtils::EmptyString;
+ value["progress"] = Progress();
+ value["progresspercentage"] = ProgressPercentage();
+ value["episodename"] = m_strEpisodeName;
+ value["episodenum"] = m_iEpisodeNumber;
+ value["episodepart"] = m_iEpisodePart;
+ value["hastimer"] = HasTimer();
+ value["isactive"] = IsActive();
+ value["wasactive"] = WasActive();
}
bool CEpgInfoTag::Changed(void) const
m_iChannelGroup = EPG_SEARCH_UNSET;
m_bIgnorePresentTimers = true;
m_bIgnorePresentRecordings = true;
+ m_iUniqueBroadcastId = EPG_SEARCH_UNSET;
}
bool EpgSearchFilter::MatchGenre(const CEpgInfoTag &tag) const
return bReturn;
}
+bool EpgSearchFilter::MatchBroadcastId(const CEpgInfoTag &tag) const
+{
+ if (m_iUniqueBroadcastId != EPG_SEARCH_UNSET)
+ return (tag.UniqueBroadcastID() == m_iUniqueBroadcastId);
+
+ return true;
+}
+
bool EpgSearchFilter::FilterEntry(const CEpgInfoTag &tag) const
{
return (MatchGenre(tag) &&
+ MatchBroadcastId(tag) &&
MatchDuration(tag) &&
MatchStartAndEndTimes(tag) &&
MatchSearchTerm(tag)) &&
(!tag.HasPVRChannel() ||
- (MatchChannelNumber(tag) &&
- MatchChannelGroup(tag) &&
- (!m_bFTAOnly || !tag.ChannelTag()->IsEncrypted())));
+ (MatchChannelNumber(tag) &&
+ MatchChannelGroup(tag) &&
+ (!m_bFTAOnly || !tag.ChannelTag()->IsEncrypted())));
}
int EpgSearchFilter::RemoveDuplicates(CFileItemList &results)
virtual bool MatchSearchTerm(const CEpgInfoTag &tag) const;
virtual bool MatchChannelNumber(const CEpgInfoTag &tag) const;
virtual bool MatchChannelGroup(const CEpgInfoTag &tag) const;
+ virtual bool MatchBroadcastId(const CEpgInfoTag &tag) const;
static int RemoveDuplicates(CFileItemList &results);
int m_iChannelGroup; /*!< The group this channel belongs to */
bool m_bIgnorePresentTimers; /*!< True to ignore currently present timers (future recordings), false if not */
bool m_bIgnorePresentRecordings; /*!< True to ignore currently active recordings, false if not */
+ int m_iUniqueBroadcastId; /*!< The broadcastid to search for */
};
}
m_crossFadeTime = 1;
}
-void CGUIImage::SetFileName(const CStdString& strFileName, bool setConstant)
+void CGUIImage::SetFileName(const CStdString& strFileName, bool setConstant, const bool useCache)
{
if (setConstant)
m_info.SetLabel(strFileName, "", GetParentID());
+ // Set whether or not to use cache
+ m_texture.SetUseCache(useCache);
+
if (m_crossFadeTime)
{
// set filename on the next texture
virtual void UpdateInfo(const CGUIListItem *item = NULL);
virtual void SetInfo(const CGUIInfoLabel &info);
- virtual void SetFileName(const CStdString& strFileName, bool setConstant = false);
+ virtual void SetFileName(const CStdString& strFileName, bool setConstant = false, const bool useCache = true);
virtual void SetAspectRatio(const CAspectRatio &aspect);
virtual void SetWidth(float width);
virtual void SetHeight(float height);
filename = right.filename;
useLarge = right.useLarge;
diffuseColor = right.diffuseColor;
-
return *this;
}
m_allocateDynamically = false;
m_isAllocated = NO;
m_invalid = true;
+ m_use_cache = true;
}
CGUITextureBase::CGUITextureBase(const CGUITextureBase &right) :
m_diffuseColor = right.m_diffuseColor;
m_allocateDynamically = right.m_allocateDynamically;
+ m_use_cache = right.m_use_cache;
// defaults
m_vertex.SetRect(m_posX, m_posY, m_posX + m_width, m_posY + m_height);
if (m_isAllocated != NORMAL)
{ // use our large image background loader
CTextureArray texture;
- if (g_largeTextureManager.GetImage(m_info.filename, texture, !IsAllocated()))
+ if (g_largeTextureManager.GetImage(m_info.filename, texture, !IsAllocated(), m_use_cache))
{
m_isAllocated = LARGE;
return true;
}
+void CGUITextureBase::SetUseCache(const bool useCache)
+{
+ m_use_cache = useCache;
+}
+
int CGUITextureBase::GetOrientation() const
{
// multiply our orientations
bool SetWidth(float width);
bool SetHeight(float height);
bool SetFileName(const CStdString &filename);
+ void SetUseCache(const bool useCache = true);
bool SetAspectRatio(const CAspectRatio &aspect);
const CStdString& GetFileName() const { return m_info.filename; };
CRect m_vertex; // vertex coords to render
bool m_invalid; // if true, we need to recalculate
-
+ bool m_use_cache;
unsigned char m_alpha;
float m_frameWidth, m_frameHeight; // size in pixels of the actual frame within the texture
#include "SDLJoystick.h"
#endif
+#define JOYSTICK_DEFAULT_MAP "_xbmc_"
+
using namespace std;
using namespace XFILE;
#if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
void CButtonTranslator::MapJoystickActions(int windowID, TiXmlNode *pJoystick)
{
- string joyname = "_xbmc_"; // default global map name
+ string joyname = JOYSTICK_DEFAULT_MAP; // default global map name
vector<string> joynames;
map<int, string> buttonMap;
map<int, string> axisMap;
map<string, JoystickMap>::iterator it = jmap->find(szDevice);
if (it==jmap->end())
- return false;
+ {
+ it = jmap->find(JOYSTICK_DEFAULT_MAP); // default global map name
+ if (it==jmap->end())
+ return false;
+ }
JoystickMap wmap = it->second;
#include "music/MusicThumbLoader.h"
#include "Util.h"
#include "pvr/channels/PVRChannel.h"
+#include "epg/Epg.h"
+#include "epg/EpgContainer.h"
using namespace MUSIC_INFO;
using namespace JSONRPC;
if (allowFile)
{
if (item->HasVideoInfoTag() && !item->GetVideoInfoTag()->GetPath().IsEmpty())
- object["file"] = item->GetVideoInfoTag()->GetPath().c_str();
+ object["file"] = item->GetVideoInfoTag()->GetPath().c_str();
if (item->HasMusicInfoTag() && !item->GetMusicInfoTag()->GetURL().IsEmpty())
object["file"] = item->GetMusicInfoTag()->GetURL().c_str();
{
if (item->HasPVRChannelInfoTag() && item->GetPVRChannelInfoTag()->ChannelID() > 0)
object[ID] = item->GetPVRChannelInfoTag()->ChannelID();
+ else if (item->HasEPGInfoTag() && item->GetEPGInfoTag()->UniqueBroadcastID() > 0)
+ object[ID] = item->GetEPGInfoTag()->UniqueBroadcastID();
else if (item->HasMusicInfoTag() && item->GetMusicInfoTag()->GetDatabaseId() > 0)
object[ID] = (int)item->GetMusicInfoTag()->GetDatabaseId();
else if (item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_iDbId > 0)
if (item->HasPVRChannelInfoTag())
FillDetails(item->GetPVRChannelInfoTag(), item, fields, object, thumbLoader);
+ if (item->HasEPGInfoTag())
+ FillDetails(item->GetEPGInfoTag(), item, fields, object, thumbLoader);
if (item->HasVideoInfoTag())
FillDetails(item->GetVideoInfoTag(), item, fields, object, thumbLoader);
if (item->HasMusicInfoTag())
{ "PVR.GetChannelGroupDetails", CPVROperations::GetChannelGroupDetails },
{ "PVR.GetChannels", CPVROperations::GetChannels },
{ "PVR.GetChannelDetails", CPVROperations::GetChannelDetails },
+ { "PVR.GetBroadcasts", CPVROperations::GetBroadcasts },
+ { "PVR.GetBroadcastDetails", CPVROperations::GetBroadcastDetails },
{ "PVR.Record", CPVROperations::Record },
{ "PVR.Scan", CPVROperations::Scan },
return OK;
}
+JSONRPC_STATUS CPVROperations::GetBroadcasts(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result)
+{
+ if (!g_PVRManager.IsStarted())
+ return FailedToExecute;
+
+ CPVRChannelGroupsContainer *channelGroupContainer = g_PVRManager.ChannelGroups();
+ if (channelGroupContainer == NULL)
+ return FailedToExecute;
+
+ CPVRChannelPtr channel = channelGroupContainer->GetChannelById((int)parameterObject["channelid"].asInteger());
+ if (channel == NULL)
+ return InvalidParams;
+
+ CEpg *channelEpg = channel->GetEPG();
+ if (channelEpg == NULL)
+ return InternalError;
+
+ CFileItemList programFull;
+ channelEpg->Get(programFull);
+
+ HandleFileItemList("broadcastid", false, "broadcasts", programFull, parameterObject, result, programFull.Size(), true);
+
+ return OK;
+}
+
+JSONRPC_STATUS CPVROperations::GetBroadcastDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result)
+{
+ if (!g_PVRManager.IsStarted())
+ return FailedToExecute;
+
+ EpgSearchFilter filter;
+ filter.Reset();
+ filter.m_iUniqueBroadcastId = (int)parameterObject["broadcastid"].asInteger();
+
+ CFileItemList broadcasts;
+ int resultSize = g_EpgContainer.GetEPGSearch(broadcasts, filter);
+
+ if (resultSize <= 0)
+ return InvalidParams;
+ else if (resultSize > 1)
+ return InternalError;
+
+ CFileItemPtr broadcast = broadcasts.Get(0);
+ HandleFileItem("broadcastid", false, "broadcastdetails", broadcast, parameterObject, parameterObject["properties"], result, false);
+
+ return OK;
+}
+
+
JSONRPC_STATUS CPVROperations::Record(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result)
{
if (!g_PVRManager.IsStarted())
if (!g_PVRManager.ToggleRecordingOnChannel(pChannel->ChannelID()))
return FailedToExecute;
}
-
+
return ACK;
}
result = object;
}
-}
\ No newline at end of file
+}
static JSONRPC_STATUS GetChannelGroupDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result);
static JSONRPC_STATUS GetChannels(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result);
static JSONRPC_STATUS GetChannelDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result);
+ static JSONRPC_STATUS GetBroadcasts(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result);
+ static JSONRPC_STATUS GetBroadcastDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result);
static JSONRPC_STATUS Record(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result);
static JSONRPC_STATUS Scan(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result);
static JSONRPC_STATUS GetPropertyValue(const CStdString &property, CVariant &result);
static void FillChannelGroupDetails(const PVR::CPVRChannelGroupPtr &channelGroup, const CVariant ¶meterObject, CVariant &result, bool append = false);
};
-}
\ No newline at end of file
+}
namespace JSONRPC
{
const char* const JSONRPC_SERVICE_ID = "http://xbmc.org/jsonrpc/ServiceDescription.json";
- const char* const JSONRPC_SERVICE_VERSION = "6.6.3";
+ const char* const JSONRPC_SERVICE_VERSION = "6.7.0";
const char* const JSONRPC_SERVICE_DESCRIPTION = "JSON-RPC API of XBMC";
const char* const JSONRPC_SERVICE_TYPES[] = {
"}"
"}"
"}",
+ "\"PVR.Fields.Broadcast\": {"
+ "\"extends\": \"Item.Fields.Base\","
+ "\"items\": { \"type\": \"string\","
+ "\"enum\": [ \"title\", \"plot\", \"plotoutline\", \"starttime\","
+ "\"endtime\", \"runtime\", \"progress\", \"progresspercentage\","
+ "\"genre\", \"episodename\", \"episodenum\", \"episodepart\","
+ "\"firstaired\", \"hastimer\", \"isactive\", \"parentalrating\","
+ "\"wasactive\", \"thumbnail\" ]"
+ "}"
+ "}",
+ "\"PVR.Details.Broadcast\": {"
+ "\"extends\": \"Item.Details.Base\","
+ "\"properties\": {"
+ "\"broadcastid\": { \"$ref\": \"Library.Id\", \"required\": true },"
+ "\"title\": { \"type\": \"string\" },"
+ "\"plot\": { \"type\": \"string\" },"
+ "\"plotoutline\": { \"type\": \"string\" },"
+ "\"start\": { \"type\": \"string\" },"
+ "\"end\": { \"type\": \"string\" },"
+ "\"duration\": { \"type\": \"integer\" },"
+ "\"progress\": { \"type\": \"integer\" },"
+ "\"progresspercentage\": { \"type\": \"number\" },"
+ "\"path\": { \"type\": \"string\" },"
+ "\"genre\": { \"type\": \"string\" },"
+ "\"episodename\": { \"type\": \"string\" },"
+ "\"episodenum\": { \"type\": \"integer\" },"
+ "\"episodepart\": { \"type\": \"integer\" },"
+ "\"firstaired\": { \"type\": \"string\" },"
+ "\"hastimer\": { \"type\": \"boolean\" },"
+ "\"isactive\": { \"type\": \"boolean\" },"
+ "\"parentalrating\": { \"type\": \"integer\" },"
+ "\"wasactive\": { \"type\": \"boolean\" },"
+ "\"thumbnail\": { \"type\": \"string\" }"
+ "}"
+ "}",
"\"Profiles.Password\": {"
"\"type\": \"object\","
"\"properties\": {"
"}"
"}"
"}",
+ "\"PVR.GetBroadcasts\": {"
+ "\"type\": \"method\","
+ "\"description\": \"Retrieves the program of a specific channel\","
+ "\"transport\": \"Response\","
+ "\"permission\": \"ReadData\","
+ "\"params\": ["
+ "{ \"name\": \"channelid\", \"$ref\": \"Library.Id\", \"required\": true },"
+ "{ \"name\": \"properties\", \"$ref\": \"PVR.Fields.Broadcast\" },"
+ "{ \"name\": \"limits\", \"$ref\": \"List.Limits\" }"
+ "],"
+ "\"returns\": { \"type\": \"object\","
+ "\"properties\": {"
+ "\"limits\": { \"$ref\": \"List.LimitsReturned\", \"required\": true },"
+ "\"broadcasts\": { \"type\": \"array\", \"required\": true,"
+ "\"items\": { \"$ref\": \"PVR.Details.Broadcast\" }"
+ "}"
+ "}"
+ "}"
+ "}",
+ "\"PVR.GetBroadcastDetails\": {"
+ "\"type\": \"method\","
+ "\"description\": \"Retrieves the details of a specific broadcast\","
+ "\"transport\": \"Response\","
+ "\"permission\": \"ReadData\","
+ "\"params\": ["
+ "{ \"name\": \"broadcastid\", \"$ref\": \"Library.Id\", \"required\": true },"
+ "{ \"name\": \"properties\", \"$ref\": \"PVR.Fields.Broadcast\" }"
+ "],"
+ "\"returns\": { \"type\": \"object\","
+ "\"properties\": {"
+ "\"broadcastdetails\": { \"$ref\": \"PVR.Details.Broadcast\" }"
+ "}"
+ "}"
+ "}",
"\"PVR.Record\": {"
"\"type\": \"method\","
"\"description\": \"Toggle recording of a channel\","
}
}
},
+ "PVR.GetBroadcasts": {
+ "type": "method",
+ "description": "Retrieves the program of a specific channel",
+ "transport": "Response",
+ "permission": "ReadData",
+ "params": [
+ { "name": "channelid", "$ref": "Library.Id", "required": true },
+ { "name": "properties", "$ref": "PVR.Fields.Broadcast" },
+ { "name": "limits", "$ref": "List.Limits" }
+ ],
+ "returns": { "type": "object",
+ "properties": {
+ "limits": { "$ref": "List.LimitsReturned", "required": true },
+ "broadcasts": { "type": "array", "required": true,
+ "items": { "$ref": "PVR.Details.Broadcast" }
+ }
+ }
+ }
+ },
+ "PVR.GetBroadcastDetails": {
+ "type": "method",
+ "description": "Retrieves the details of a specific broadcast",
+ "transport": "Response",
+ "permission": "ReadData",
+ "params": [
+ { "name": "broadcastid", "$ref": "Library.Id", "required": true },
+ { "name": "properties", "$ref": "PVR.Fields.Broadcast" }
+ ],
+ "returns": { "type": "object",
+ "properties": {
+ "broadcastdetails": { "$ref": "PVR.Details.Broadcast" }
+ }
+ }
+ },
"PVR.Record": {
"type": "method",
"description": "Toggle recording of a channel",
}
}
},
+ "PVR.Fields.Broadcast": {
+ "extends": "Item.Fields.Base",
+ "items": { "type": "string",
+ "enum": [ "title", "plot", "plotoutline", "starttime",
+ "endtime", "runtime", "progress", "progresspercentage",
+ "genre", "episodename", "episodenum", "episodepart",
+ "firstaired", "hastimer", "isactive", "parentalrating",
+ "wasactive", "thumbnail" ]
+ }
+ },
+ "PVR.Details.Broadcast": {"
+ "extends": "Item.Details.Base",
+ "properties": {"
+ "broadcastid": { "$ref": "Library.Id", "required": true },
+ "title": { "type": "string" },
+ "plot": { "type": "string" },
+ "plotoutline": { "type": "string" },
+ "starttime": { "type": "string" },
+ "endtime": { "type": "string" },
+ "runtime": { "type": "integer" },
+ "progress": { "type": "integer" },
+ "progresspercentage": { "type": "number" },
+ "genre": { "type": "string" },
+ "episodename": { "type": "string" },
+ "episodenum": { "type": "integer" },
+ "episodepart": { "type": "integer" },
+ "firstaired": { "type": "string" },
+ "hastimer": { "type": "boolean" },
+ "isactive": { "type": "boolean" },
+ "rating": { "type": "integer" },
+ "wasactive": { "type": "boolean" },
+ "thumbnail": { "type": "string" }
+ }
+ },
"Profiles.Password": {
"type": "object",
"properties": {
sscanf(_colorDiffuse, "%x", &colorDiffuse);
}
- void ControlImage::setImage(const char* imageFilename) throw (UnimplementedException)
+ void ControlImage::setImage(const char* imageFilename, const bool useCache) throw (UnimplementedException)
{
strFileName = imageFilename;
LOCKGUI;
if (pGUIControl)
- ((CGUIImage*)pGUIControl)->SetFileName(strFileName);
+ ((CGUIImage*)pGUIControl)->SetFileName(strFileName, false, useCache);
}
void ControlImage::setColorDiffuse(const char* cColorDiffuse) throw (UnimplementedException)
const char* colorDiffuse = NULL);
/**
- * setImage(filename) -- Changes the image.
+ * setImage(filename, useCache) -- Changes the image.
*
* filename : string - image filename.
+ * useCache : [opt] bool - true/use cache, false/don't use cache
*
* example:
* - self.image.setImage('special://home/scripts/test.png')
*/
- virtual void setImage(const char* imageFilename) throw (UnimplementedException);
+ virtual void setImage(const char* imageFilename, const bool useCache = true) throw (UnimplementedException);
/**
* setColorDiffuse(colorDiffuse) -- Changes the images color.
controlAttributes |= (int)SettingControlAttributeHidden;
else if (StringUtils::EqualsNoCase(*attribute, "new"))
controlAttributes |= (int)SettingControlAttributeVerifyNew;
+ else if (StringUtils::EqualsNoCase(*attribute, "hide_value"))
+ controlAttributes |= (int)SettingControlAttributeHideValue;
else
return false;
}
typedef enum {
SettingControlAttributeNone = 0x0,
SettingControlAttributeHidden = 0x1,
- SettingControlAttributeVerifyNew = 0x2
+ SettingControlAttributeVerifyNew = 0x2,
+ SettingControlAttributeHideValue = 0x4
} SettingControlAttribute;
class CSettingControl
CGUIControlBaseSetting::Update();
- if (m_pSetting->GetType() == SettingTypeString)
+ if (m_pSetting->GetType() == SettingTypeString &&
+ !(m_pSetting->GetControl().GetAttributes() & SettingControlAttributeHideValue))
{
std::string strText = ((CSettingString *)m_pSetting)->GetValue();
switch (m_pSetting->GetControl().GetFormat())
}
default:
- return;
+ break;
}
m_pButton->SetLabel2(strText);