diff options
author | cleichner <cleichner@chromium.org> | 2016-02-02 10:17:58 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-02-02 18:19:33 +0000 |
commit | 39c6d32f58f89a1ae9666b31e2f799037c0b8830 (patch) | |
tree | 6f3d4e45e84339b4ee2028722be67ee59a3f8ae2 /chromecast | |
parent | 66f50590116332a7d4cbda9e4eaae81b1274944c (diff) | |
download | chromium_src-39c6d32f58f89a1ae9666b31e2f799037c0b8830.zip chromium_src-39c6d32f58f89a1ae9666b31e2f799037c0b8830.tar.gz chromium_src-39c6d32f58f89a1ae9666b31e2f799037c0b8830.tar.bz2 |
[Cast ALSA CMA] Replace ioctls with ALSA controls
In order to more accurately synchronize audio output on multiple devices
the driver exposes the ability to slightly adjust the clock rate.
Previously, this clock adjustment was done through device-specific
ioctls, but the driver has been modified to use ALSA control elements
instead. This changes the multiroom userspace code to use the ALSA
controls instead of the device-specific ioctls.
BUG=interal b/25601063
TEST=Ran multizone and verified that the error trends downwards over
time
Review URL: https://codereview.chromium.org/1646493003
Cr-Commit-Position: refs/heads/master@{#372988}
Diffstat (limited to 'chromecast')
-rw-r--r-- | chromecast/media/cma/backend/alsa/cast_media_shlib.cc | 111 |
1 files changed, 73 insertions, 38 deletions
diff --git a/chromecast/media/cma/backend/alsa/cast_media_shlib.cc b/chromecast/media/cma/backend/alsa/cast_media_shlib.cc index 0ac44a7..93f4537 100644 --- a/chromecast/media/cma/backend/alsa/cast_media_shlib.cc +++ b/chromecast/media/cma/backend/alsa/cast_media_shlib.cc @@ -2,10 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <fcntl.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <unistd.h> +#include <alsa/asoundlib.h> #include "base/at_exit.h" #include "base/command_line.h" @@ -21,22 +18,28 @@ #include "chromecast/public/video_plane.h" #include "media/base/media.h" +#define RETURN_ON_ALSA_ERROR(snd_func, ...) \ + do { \ + int err = snd_func(__VA_ARGS__); \ + if (err < 0) { \ + LOG(ERROR) << #snd_func " error: " << snd_strerror(err); \ + return; \ + } \ + } while (0) + namespace chromecast { namespace media { namespace { -const char kPepperoniAlsaDevName[] = "/dev/snd/hwC0D0"; +const char kDefaultPcmDevice[] = "hw:0"; +const int kSoundControlBlockingMode = 0; +const char kRateOffsetInterfaceName[] = "PCM Playback Rate Offset"; // 1 MHz reference allows easy translation between frequency and PPM. const double kOneMhzReference = 1e6; const double kMaxAdjustmentHz = 500; const double kGranularityHz = 1.0; -enum { - SNDRV_BERLIN_GET_CLOCK_PPM = 0x1003, - SNDRV_BERLIN_SET_CLOCK_PPM, -}; - class DefaultVideoPlane : public VideoPlane { public: ~DefaultVideoPlane() override {} @@ -45,6 +48,28 @@ class DefaultVideoPlane : public VideoPlane { Transform transform) override {} }; +snd_hctl_t* g_hardware_controls = nullptr; +snd_ctl_elem_id_t* g_rate_offset_id = nullptr; +snd_ctl_elem_value_t* g_rate_offset_ppm = nullptr; +snd_hctl_elem_t* g_rate_offset_element = nullptr; + +void InitializeAlsaControls() { + RETURN_ON_ALSA_ERROR(snd_ctl_elem_id_malloc, &g_rate_offset_id); + RETURN_ON_ALSA_ERROR(snd_ctl_elem_value_malloc, &g_rate_offset_ppm); + RETURN_ON_ALSA_ERROR(snd_hctl_open, &g_hardware_controls, kDefaultPcmDevice, + kSoundControlBlockingMode); + RETURN_ON_ALSA_ERROR(snd_hctl_load, g_hardware_controls); + snd_ctl_elem_id_set_interface(g_rate_offset_id, SND_CTL_ELEM_IFACE_PCM); + snd_ctl_elem_id_set_name(g_rate_offset_id, kRateOffsetInterfaceName); + g_rate_offset_element = + snd_hctl_find_elem(g_hardware_controls, g_rate_offset_id); + if (g_rate_offset_element) { + snd_ctl_elem_value_set_id(g_rate_offset_ppm, g_rate_offset_id); + } else { + LOG(ERROR) << "snd_hctl_find_elem failed to find the rate offset element."; + } +} + DefaultVideoPlane* g_video_plane = nullptr; base::AtExitManager g_at_exit_manager; @@ -59,12 +84,23 @@ void CastMediaShlib::Initialize(const std::vector<std::string>& argv) { g_video_plane = new DefaultVideoPlane(); + InitializeAlsaControls(); ::media::InitializeMediaLibrary(); } void CastMediaShlib::Finalize() { base::CommandLine::Reset(); + if (g_hardware_controls) + snd_hctl_close(g_hardware_controls); + snd_ctl_elem_value_free(g_rate_offset_ppm); + snd_ctl_elem_id_free(g_rate_offset_id); + + g_hardware_controls = nullptr; + g_rate_offset_id = nullptr; + g_rate_offset_ppm = nullptr; + g_rate_offset_element = nullptr; + delete g_video_plane; g_video_plane = nullptr; @@ -94,13 +130,20 @@ MediaPipelineBackend* CastMediaShlib::CreateMediaPipelineBackend( } double CastMediaShlib::GetMediaClockRate() { - base::ScopedFD alsa_device( - TEMP_FAILURE_RETRY(open(kPepperoniAlsaDevName, O_RDONLY))); - DCHECK(alsa_device.is_valid()) << "Failed to open ALSA device"; - double ppm; - int ret = ioctl(alsa_device.get(), SNDRV_BERLIN_GET_CLOCK_PPM, &ppm); - DCHECK(ret != -1) << "ioctl(SNDRV_BERLIN_GET_CLOCK_PPM) failed: " - << strerror(errno); + int ppm = 0; + if (!g_rate_offset_element) { + VLOG(1) << "g_rate_offset_element is null, ALSA rate offset control will " + "not be possible."; + return kOneMhzReference; + } + snd_ctl_elem_value_t* rate_offset_ppm; + snd_ctl_elem_value_alloca(&rate_offset_ppm); + int err = snd_hctl_elem_read(g_rate_offset_element, rate_offset_ppm); + if (err < 0) { + LOG(ERROR) << "snd_htcl_elem_read error: " << snd_strerror(err); + return kOneMhzReference; + } + ppm = snd_ctl_elem_value_get_integer(rate_offset_ppm, 0); return kOneMhzReference + ppm; } @@ -118,33 +161,25 @@ void CastMediaShlib::MediaClockRateRange(double* minimum_rate, } bool CastMediaShlib::SetMediaClockRate(double new_rate) { - new_rate -= kOneMhzReference; - base::ScopedFD alsa_device( - TEMP_FAILURE_RETRY(open(kPepperoniAlsaDevName, O_WRONLY))); - DCHECK(alsa_device.is_valid()) << "Failed to open ALSA device"; - - int ret = ioctl(alsa_device.get(), SNDRV_BERLIN_SET_CLOCK_PPM, &new_rate); - if (ret) { - LOG(ERROR) << "ioctl(SNDRV_BERLIN_SET_CLOCK_PPM) failed: " - << strerror(errno); + int new_ppm = new_rate - kOneMhzReference; + if (!g_rate_offset_element) { + VLOG(1) << "g_rate_offset_element is null, ALSA rate offset control will " + "not be possible."; + return false; + } + snd_ctl_elem_value_t* rate_offset_ppm; + snd_ctl_elem_value_alloca(&rate_offset_ppm); + snd_ctl_elem_value_set_integer(rate_offset_ppm, 0, new_ppm); + int err = snd_hctl_elem_write(g_rate_offset_element, rate_offset_ppm); + if (err < 0) { + LOG(ERROR) << "snd_htcl_elem_write error: " << snd_strerror(err); return false; } return true; } bool CastMediaShlib::SupportsMediaClockRateChange() { - // Simply test the 'Get PPM' IOCTL to determine support. - base::ScopedFD alsa_device( - TEMP_FAILURE_RETRY(open(kPepperoniAlsaDevName, O_RDONLY))); - if (!alsa_device.is_valid()) - return false; - - double ppm; - int ret = ioctl(alsa_device.get(), SNDRV_BERLIN_GET_CLOCK_PPM, &ppm); - if (ret == -1) - return false; - - return true; + return g_rate_offset_element != nullptr; } } // namespace media |