Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simulataneous Audio Over ALSA and Local #498

Open
scotthardwick opened this issue Nov 12, 2016 · 16 comments
Open

Simulataneous Audio Over ALSA and Local #498

scotthardwick opened this issue Nov 12, 2016 · 16 comments

Comments

@scotthardwick
Copy link

I have been researching on Google and I seem to keep coming up that at least in the past it was not possible to get simultaneous audio to HDMI and I2C.

Is there a way to get simultaneous output to Local jack and ALSA (in my case I am using a Hifiberry Amp+)

Thanks for any input!

Best!

Scott

@popcornmix
Copy link
Owner

Not currently possible. Some discussion here: #463
@fabled may be explain exactly what is still required.

@fabled
Copy link

fabled commented Nov 15, 2016

Well, pretty much all of the code works. It would just require specifying the command line parameters and implementing the small bit of glue code. The format it was originally was not good enough, so I'm open for suggestions. The current options are: local, hdmi, both and alsa. We'd need something to make both use alsa, or add new target name such as 'alsa+hdmi' to specify output on both. Suggestions?

@popcornmix
Copy link
Owner

-o alsa+hdmi is good for me.

@scotthardwick
Copy link
Author

My personal thanks to both of you! Your coding skills and knowledge of how all this truly is beyond impressive to someone who is much better categorized as a "novice".

-o local
-o hdmi
-o hdmi+local (if you want to alias "both")
-o alsa
-o alsa+local
-o alsa+hdmi

fabled added a commit to fabled/omxplayer that referenced this issue Nov 16, 2016
@scotthardwick
Copy link
Author

Fabled---
Could you please also add Alsa+local support?
Thank You!

@fabled
Copy link

fabled commented Nov 17, 2016

@scotthardwick alsa+local is slightly more complex. It would require setting up additional OMX switching logic. Also I wonder should alsa or local output be the clock master.
@popcornmix Care to review the pull request for alsa+hdmi? Any suggestions how to do splitting and clocking for alsa+local output? Would we also need alsa+local+hdmi?

@scotthardwick
Copy link
Author

@fabled -- I would think alsa would be the clock master in that situation. But that is just my gut thought. Thank you for continuing to investigate this for me!

@jehutting
Copy link

I have been hesitating to comment on this as I was already late at the party...
But still seeing that it is not implemented/pulled in yet, I will give my comment for what it is worth :-)

Isn't it better to have a comma separated list of devices you want to use? E.g. -o local,hdmi for both the local and hdmi, or -o alsa:snd2,local for both local and alsa device named snd2.

The notation alsa+hdmi[:device] looks more specifying the hdmi device, whereas specifying the alsa device is meant.

@fabled
Copy link

fabled commented Dec 27, 2016

@jehutting Sounds good to me.
@popcornmix Any suggestions how to modify to code the pass a list of sound outputs instead of single device name?

@popcornmix
Copy link
Owner

I'm happy with @jehutting's syntax.
I think I'd probably make the OMXAudioConfig.device become the comma separated string.
I'd remove the "omx:" prefix as I don't see that being essential for the omxplayer.cpp -> OMXAudio.cpp API.
Existing command line option -o both would behave the same as -o hdmi,local so internally I'd pass hdmi,local and treat both just as a shortcut that is expanded by omxplayer.cpp.
Code like

  if (m_config.device == "omx:both" || m_config.device == "omx:hdmi")
  {
    if(!m_omx_render_hdmi.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit))
      return false;
  }

would become

  if (m_config.device.find("hdmi") != std::string::npos)
  {
    if(!m_omx_render_hdmi.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit))
      return false;
  }

(a helper function for checking if m_config.device enables "hdmi" etc is also valid)
Up to you if you now parse the alsa subdevice from the device string inside OMXAudio.cpp (perhaps with a helper function) rather than using subdevice.

@jehutting
Copy link

I'm using (in my own, not-gihub'ed, not public'ed) omxplayer the following code:

  // audio out device
  {
  std::vector<std::string> devices;
  boost::split(devices, settings.device_string, boost::is_any_of(","), boost::token_compress_on);

  m_config_audio.devices = OMXAudioConfig::AudioDevice::NONE;
  for(auto it = devices.begin(); it != devices.end(); it++)
  {
    #define device (*it)
    if(device == "both")
      m_config_audio.devices |= (OMXAudioConfig::AudioDevice::LOCAL|OMXAudioConfig::AudioDevice::HDMI);
    else if(device == "local")
      m_config_audio.devices |= OMXAudioConfig::AudioDevice::LOCAL;
    else if(device == "hdmi")
      m_config_audio.devices |= OMXAudioConfig::AudioDevice::HDMI;
    else if(device.substr(0, 4) == "alsa")
    {
      m_config_audio.devices |= OMXAudioConfig::AudioDevice::ALSA;
      // check for alsa device name
      if(device[4] == ':')
        m_config_audio.alsa_device_name = device.substr(4+1/* to skip ':' */);
    }
  }
  }

Changed to using string.find() -just for fun-:

  // audio out device
  m_config_audio.devices = OMXAudioConfig::AudioDevice::NONE;
  if(settings.device_string.find("both") != std::string::npos)
    m_config_audio.devices = (OMXAudioConfig::AudioDevice::LOCAL|OMXAudioConfig::AudioDevice::HDMI);
  else 
  {
    if(settings.device_string.find("local") != std::string::npos)
      m_config_audio.devices = OMXAudioConfig::AudioDevice::LOCAL;
    if(settings.device_string.find("hdmi") != std::string::npos)
      m_config_audio.devices |= OMXAudioConfig::AudioDevice::HDMI;
  }
  size_t pos = settings.device_string.find("alsa");
  if(pos != std::string::npos)
  {
    m_config_audio.devices |= OMXAudioConfig::AudioDevice::ALSA;
    pos += 4; // position right behind "alsa"
    // check for alsa device name
    if(settings.device_string[pos] == ':')
    {
      pos++; // name without the ':' character
      size_t end = settings.device_string.find(',', pos);  
      if(end == std::string::npos) // get name till end of device_string...
        m_config_audio.alsa_device_name = settings.device_string.substr(pos);
      else // ...or till (found) separator
        m_config_audio.alsa_device_name = settings.device_string.substr(pos, end-pos);
    }
  }

The AudioDevice class defined in OMXAudio.h:

class OMXAudioConfig
{
public:
  enum class AudioDevice : std::uint8_t { NONE=0x00, LOCAL=0x01, HDMI=0x02, ALSA=0x04 };
  friend bool operator&(AudioDevice lhs, AudioDevice rhs) { return ((uint8_t)lhs & (uint8_t)rhs) ? true : false; }
  friend AudioDevice operator|(const AudioDevice lhs, const AudioDevice rhs) { return static_cast<AudioDevice>((uint8_t)lhs | (uint8_t)rhs); }
  friend AudioDevice& operator|=(AudioDevice& lhs, const AudioDevice rhs) { return lhs = static_cast<AudioDevice>((uint8_t)lhs | (uint8_t)rhs); }

  COMXStreamInfo hints;
  AudioDevice devices;
  std::string alsa_device_name;
  ...

  OMXAudioConfig()
  {
    devices = AudioDevice::NONE;
    alsa_device_name = "default";
    ...
  }
};

A snapshot of its usage (in OMXAudio.cpp)

  //   S P L I T T E R   I N I T I A L I S A T I O N
  // Only when two or more render components are used

  int ndevices = 0;
  if(m_config.devices & OMXAudioConfig::AudioDevice::LOCAL)
    ndevices++;
  if(m_config.devices & OMXAudioConfig::AudioDevice::HDMI)
    ndevices++;
  if(m_config.devices & OMXAudioConfig::AudioDevice::ALSA)
    ndevices++;
  if(ndevices > 1)
  {
    if(!m_omx_splitter.Initialize("OMX.broadcom.audio_splitter", OMX_IndexParamAudioInit))
    {
      CLog::Log(LOGERROR, "COMXAudio::PortSettingsChanged() FAIL audio_splitter OMX_IndexParamAudioInit\n");
      return false;
    }
  }

  //   R E N D E R   I N I T I A L I S A T I O N 

  if(m_config.devices & OMXAudioConfig::AudioDevice::LOCAL)
  {
    if(!m_omx_render_analog.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit))
    {
      CLog::Log(LOGERROR, "COMXAudio::PortSettingsChanged() FAIL analog audio_render OMX_IndexParamAudioInit\n");
      return false;
    }
  }
  if(m_config.devices & OMXAudioConfig::AudioDevice::HDMI)
  {
    if(!m_omx_render_hdmi.Initialize("OMX.broadcom.audio_render", OMX_IndexParamAudioInit))
    {
      CLog::Log(LOGERROR, "COMXAudio::PortSettingsChanged() FAIL hdmi audio_render OMX_IndexParamAudioInit\n");
      return false;
    }
  }

I'm describing the OpenMAX layer of omxplayer in
OMXPlayer-OpenMAX-components.pdf

Work in progress...

@fabled
Copy link

fabled commented Dec 30, 2016

I also started working on this a little bit. But I want to pass the full audio device string to OMXAudio so it can create the audio graph from it. This allows e.g. output to two alsa devices, and automatically uses the first audio device as clock master so user can decide that too. I pushed WIP code (does not compile) to fabled@feca5ce

@fabled
Copy link

fabled commented Jan 3, 2017

Ok, updated code now at fabled@cd09c41. It should mostly work, though error checking is not done fully yet.

Use -o
local,hdmi for outputting locally (clock master) and hdmi
hdmi,alsafor outputting hdmi (clock master) and alsa (default)
alsa:mydev,hdmi,local for alsa 'mydev' being clock master but copying to hdmi and local
alsa:foo,alsa:bar for two alsa devices
etc.

Maybe someone could take a look at the code changes if it looks as an idea ok? I still need to add proper error checking for omx calls and that the maximum amount of audio output devices is not exceeded.

@Ruffio
Copy link

Ruffio commented Jan 22, 2017

@fabled how is it going? It sounds like a lot has been done and only little remains. It would cool to get this implemented

@Ruffio
Copy link

Ruffio commented Apr 25, 2017

@fabled any news on this one?

@fabled
Copy link

fabled commented Apr 25, 2017

I'm waiting feedback from @popcornmix for the above mentioned commit.

dmorilha pushed a commit to dmorilha/omxplayer that referenced this issue Jan 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants