[audio] fix for multiple target support by parsing device-map json configuration 23/188123/3 accepted/tizen/unified/20180904.180637 submit/tizen/20180904.024114
authorSeungbae Shin <seungbae.shin@samsung.com>
Fri, 31 Aug 2018 11:24:08 +0000 (20:24 +0900)
committerSeungbae Shin <seungbae.shin@samsung.com>
Mon, 3 Sep 2018 11:18:46 +0000 (20:18 +0900)
[Version] 0.0.16
[Profile] Common
[Issue Type] Add
[Dependency module] N/A

Change-Id: I66909ac9942212536c5a88410ace58773536b401

configure.ac
packaging/mm-hal-interface.spec
testcase/audio/Makefile.am
testcase/audio/audio_haltests.cpp
testcase/audio/parser.cpp [new file with mode: 0644]
testcase/audio/parser.hh [new file with mode: 0644]

index 330e936..e5c30b7 100755 (executable)
@@ -48,6 +48,8 @@ AC_SUBST(TBM_LIBS)
 
 PKG_CHECK_MODULES([GST_ALLOCATORS], [gstreamer-allocators-1.0])
 
+PKG_CHECK_MODULES(LIBJSON, [ json-c >= 0.11 ])
+
 # Checks for library functions.
 AC_CONFIG_FILES([
 Makefile
index 9bd31b1..5592c56 100755 (executable)
@@ -1,6 +1,6 @@
 Name:       mm-hal-interface
 Summary:    Multimedia HAL Interface
-Version:    0.0.15
+Version:    0.0.16
 Release:    0
 Group:      Multimedia/Development
 License:    Apache-2.0
@@ -15,6 +15,7 @@ BuildRequires:  pkgconfig(libtbm)
 BuildRequires:  pkgconfig(gstreamer-1.0)
 BuildRequires:  pkgconfig(gstreamer-plugins-base-1.0)
 BuildRequires:  pkgconfig(iniparser)
+BuildRequires:  pkgconfig(json-c)
 
 %description
 Multimedia framework hardware abstraction layer interface package.
index 779de11..9bbb5e0 100644 (file)
@@ -2,11 +2,13 @@
 bin_PROGRAMS = audio_haltests
 
 audio_haltests_SOURCES = \
-       audio_haltests.cpp
+       audio_haltests.cpp \
+       parser.cpp
 
 audio_haltests_CPPFLAGS = \
        $(DLOG_CFLAGS)\
        $(SYSTEM_INFO_CFLAGS)\
+       $(LIBJSON_CFLAGS)\
        -I$(srcdir)/../../include/audio
 
 audio_haltests_LDADD = \
@@ -14,5 +16,6 @@ audio_haltests_LDADD = \
        -lgtest\
        $(top_builddir)/src/audio/libaudio_hal_interface.la\
        $(DLOG_LIBS)\
+       $(LIBJSON_LIBS)\
        $(SYSTEM_INFO_LIBS)
 
index 4a9f628..0e21ad3 100644 (file)
@@ -31,6 +31,8 @@
 #include <audio_hal_interface.h>
 #include <system_info.h>
 
+#include "parser.hh"
+
 using namespace std;
 
 #define USE_IFSTREAM
@@ -88,10 +90,8 @@ class AudioHalTest : public testing::Test
                // Todo : following value may vary depends on targets
                const int default_frames = 6400;
                const int default_periods = 5;
-
-               // ToDo : following should be changed in runtime
-               const string default_card = "sprdphone";
-               const string default_device = "0";
+               const int default_rate = 44100;
+               const int default_channels = 2;
 
                // ToDo : following types may need to be fixed.
                const array<string, 8> vol_types = { "system", "notification", "alarm", "ringtone",
@@ -103,10 +103,6 @@ void AudioHalTest::SetUp()
 {
        m_h = nullptr;
 
-       m_spec.format = AUDIO_SAMPLE_S16LE;
-       m_spec.rate = 44100;
-       m_spec.channels = 2;
-
        int32_t ret = audio_hal_interface_init(&m_h);
        if (ret != AUDIO_HAL_SUCCESS)
                cout << "audio hal init failed : " << ret << endl;
@@ -857,9 +853,19 @@ TEST_F(AudioHalTest, PcmGetFdP)
 {
        pcm_handle pcm_h = nullptr;
 
+       string card, dev;
+       int rate = default_rate, channels = default_channels;
+
+       CDeviceMapParser parser;
+       parser.get_playback(card, dev, rate, channels);
+
+       m_spec.format = AUDIO_SAMPLE_S16LE;
+       m_spec.rate = rate;
+       m_spec.channels = channels;
+
        SetRouteToSpeaker();
 
-       int32_t ret = audio_hal_interface_pcm_open(m_h, default_card.c_str(), default_device.c_str(),
+       int32_t ret = audio_hal_interface_pcm_open(m_h, card.c_str(), dev.c_str(),
                                                                                        DIRECTION_OUT, &m_spec, GetDefaultFrames(), default_periods, &pcm_h);
        ASSERT_EQ(ret, AUDIO_HAL_SUCCESS);
 
@@ -894,10 +900,19 @@ TEST_F(AudioHalTest, PcmGetFdN)
 
        // check for fd
        pcm_handle pcm_h = nullptr;
+       string card, dev;
+       int rate = default_rate, channels = default_channels;
+
+       CDeviceMapParser parser;
+       parser.get_playback(card, dev, rate, channels);
+
+       m_spec.format = AUDIO_SAMPLE_S16LE;
+       m_spec.rate = rate;
+       m_spec.channels = channels;
 
        SetRouteToSpeaker();
 
-       ret = audio_hal_interface_pcm_open(m_h, default_card.c_str(), default_device.c_str(),
+       ret = audio_hal_interface_pcm_open(m_h, card.c_str(), dev.c_str(),
                                                                        DIRECTION_OUT, &m_spec, GetDefaultFrames(), default_periods, &pcm_h);
        ASSERT_EQ(ret, AUDIO_HAL_SUCCESS);
 
@@ -924,10 +939,19 @@ TEST_F(AudioHalTest, PcmGetFdN)
 TEST_F(AudioHalTest, PcmOpenWriteCloseP)
 {
        pcm_handle pcm_h = nullptr;
+       string card, dev;
+       int rate = default_rate, channels = default_channels;
+
+       CDeviceMapParser parser;
+       parser.get_playback(card, dev, rate, channels);
+
+       m_spec.format = AUDIO_SAMPLE_S16LE;
+       m_spec.rate = rate;
+       m_spec.channels = channels;
 
        SetRouteToSpeaker();
 
-       int32_t ret = audio_hal_interface_pcm_open(m_h, default_card.c_str(), default_device.c_str(),
+       int32_t ret = audio_hal_interface_pcm_open(m_h, card.c_str(), dev.c_str(),
                                                                                        DIRECTION_OUT, &m_spec, GetDefaultFrames(), default_periods, &pcm_h);
        ASSERT_EQ(ret, AUDIO_HAL_SUCCESS);
 
@@ -960,10 +984,19 @@ TEST_F(AudioHalTest, PcmOpenWriteCloseP)
 TEST_F(AudioHalTest, PcmOpenReadCloseP)
 {
        pcm_handle pcm_h = nullptr;
+       string card, dev;
+       int rate = default_rate, channels = default_channels;
+
+       CDeviceMapParser parser;
+       parser.get_capture(card, dev, rate, channels);
+
+       m_spec.format = AUDIO_SAMPLE_S16LE;
+       m_spec.rate = rate;
+       m_spec.channels = channels;
 
        SetRouteToMicrophone();
 
-       int32_t ret = audio_hal_interface_pcm_open(m_h, default_card.c_str(), default_device.c_str(),
+       int32_t ret = audio_hal_interface_pcm_open(m_h, card.c_str(), dev.c_str(),
                                                                                        DIRECTION_IN, &m_spec, GetDefaultFrames(), default_periods, &pcm_h);
        ASSERT_EQ(ret, AUDIO_HAL_SUCCESS);
 
@@ -1015,10 +1048,19 @@ TEST_F(AudioHalTest, PcmRecoverN)
 TEST_F(AudioHalTest, PcmSetParamP)
 {
        pcm_handle pcm_h = nullptr;
+       string card, dev;
+       int rate = default_rate, channels = default_channels;
+
+       CDeviceMapParser parser;
+       parser.get_playback(card, dev, rate, channels);
+
+       m_spec.format = AUDIO_SAMPLE_S16LE;
+       m_spec.rate = rate;
+       m_spec.channels = channels;
 
        SetRouteToSpeaker();
 
-       int32_t ret = audio_hal_interface_pcm_open(m_h, default_card.c_str(), default_device.c_str(),
+       int32_t ret = audio_hal_interface_pcm_open(m_h, card.c_str(), dev.c_str(),
                                                                                        DIRECTION_OUT, &m_spec, GetDefaultFrames(), default_periods, &pcm_h);
        ASSERT_EQ(ret, AUDIO_HAL_SUCCESS);
 
@@ -1046,10 +1088,19 @@ TEST_F(AudioHalTest, PcmSetParamP)
 TEST_F(AudioHalTest, PcmSetParamN)
 {
        pcm_handle pcm_h = nullptr;
+       string card, dev;
+       int rate = default_rate, channels = default_channels;
+
+       CDeviceMapParser parser;
+       parser.get_playback(card, dev, rate, channels);
+
+       m_spec.format = AUDIO_SAMPLE_S16LE;
+       m_spec.rate = rate;
+       m_spec.channels = channels;
 
        SetRouteToSpeaker();
 
-       int32_t ret = audio_hal_interface_pcm_open(m_h, default_card.c_str(), default_device.c_str(),
+       int32_t ret = audio_hal_interface_pcm_open(m_h, card.c_str(), dev.c_str(),
                                                                                        DIRECTION_OUT, &m_spec, GetDefaultFrames(), default_periods, &pcm_h);
        ASSERT_EQ(ret, AUDIO_HAL_SUCCESS);
 
@@ -1076,10 +1127,19 @@ TEST_F(AudioHalTest, PcmSetParamN)
 TEST_F(AudioHalTest, PcmGetParamP)
 {
        pcm_handle pcm_h = nullptr;
+       string card, dev;
+       int rate = default_rate, channels = default_channels;
+
+       CDeviceMapParser parser;
+       parser.get_playback(card, dev, rate, channels);
+
+       m_spec.format = AUDIO_SAMPLE_S16LE;
+       m_spec.rate = rate;
+       m_spec.channels = channels;
 
        // Precondition
        SetRouteToSpeaker();
-       int32_t ret = audio_hal_interface_pcm_open(m_h, default_card.c_str(), default_device.c_str(),
+       int32_t ret = audio_hal_interface_pcm_open(m_h, card.c_str(), dev.c_str(),
                                                                                        DIRECTION_OUT, &m_spec, GetDefaultFrames(), default_periods, &pcm_h);
        ASSERT_EQ(ret, AUDIO_HAL_SUCCESS);
 
diff --git a/testcase/audio/parser.cpp b/testcase/audio/parser.cpp
new file mode 100644 (file)
index 0000000..05099ee
--- /dev/null
@@ -0,0 +1,320 @@
+#include <iostream>
+#include <json.h>
+#include <assert.h>
+#include <string.h>
+#include <string>
+
+#define DEVICE_FILE_OBJECT                  "device-files"
+#define DEVICE_TYPE_PROP_PLAYBACK_DEVICES   "playback-devices"
+#define DEVICE_TYPE_PROP_CAPTURE_DEVICES    "capture-devices"
+#define DEVICE_TYPE_PROP_DEVICE_STRING      "device-string"
+#define DEVICE_TYPE_PROP_ROLE               "role"
+
+/* device-files example
+       device-files : {
+               playback-devices : [
+                       {
+                               device-string : alsa:sprdphone,0,
+                               role : { normal : rate=44100 }
+                       }
+                       {
+                               device-string : alsa:VIRTUALAUDIOW,0,
+                               role : { call-voice : rate=16000 channels=1 tsched=0 alternate_rate=16000 }
+                       }
+               ]
+               capture-devices : [
+                       {
+                               device-string : alsa:sprdphone,0,
+                               role : { normal : rate=44100 }
+                       }
+               ]
+       }
+*/
+
+using namespace std;
+
+class CDeviceMapParser
+{
+public:
+       CDeviceMapParser();
+       CDeviceMapParser(const char* map_file);
+       virtual ~CDeviceMapParser();
+
+       void dump_devices();
+
+       void get_playback(string& card, string& device_num, int& rate, int& channels);
+       void get_capture(string& card, string& device_num, int& rate, int& channels);
+
+private:
+       void open_json(const char* json_file);
+       void close_json();
+       void parse_device_string_object(json_object *device_string_o, string& device_string);
+       void parse_device_role_object(json_object *device_role_o, string& device_params);
+       void parse_device_file_object(json_object *device_file_o, pair<string, string>& device);
+       void parse_device_file_array_object(json_object *device_file_array_o, pair<string, string>& device);
+
+       void parse_playback();
+       void parse_capture();
+       void get_device(string& s, string& card, string& device_num);
+       void get_params(string& s, int& rate, int& channels);
+       void get_single_param(string& s, int& rate, int& channels);
+
+       // FixMe, pair doens't define what is paired clearly....
+       pair<string, string> m_playback; // device_string, device_params
+       pair<string, string> m_capture;  // device_string, device_params
+
+       json_object *m_json_obj;
+       json_object *m_json_device_files_obj;
+};
+
+CDeviceMapParser::CDeviceMapParser()
+{
+       open_json("/etc/pulse/device-map.json");
+}
+
+CDeviceMapParser::CDeviceMapParser(const char* map_file)
+{
+       open_json(map_file);
+}
+
+CDeviceMapParser::~CDeviceMapParser()
+{
+       close_json();
+}
+
+void CDeviceMapParser::open_json(const char* json_file)
+{
+       m_json_obj = json_object_from_file(json_file);
+       if (!m_json_obj) {
+               cout << "Read device-map " << json_file << " failed" << endl;
+               return;
+       }
+
+       if (!json_object_object_get_ex(m_json_obj, DEVICE_FILE_OBJECT, &m_json_device_files_obj)) {
+               cout << "Get device files object failed" << endl;
+               return;
+       }
+       if (!json_object_is_type(m_json_device_files_obj, json_type_object)) {
+               cout << "json object type failed" << endl;
+               json_object_put(m_json_obj);
+               m_json_obj = NULL;
+               return;
+       }
+
+       cout << DEVICE_FILE_OBJECT << " : {" << endl;
+}
+
+void CDeviceMapParser::close_json()
+{
+       if (!m_json_obj)
+               return;
+
+       json_object_put(m_json_obj);
+
+       cout << "}" << endl;
+}
+
+void CDeviceMapParser::parse_playback()
+{
+       json_object *playback_devices_o = NULL;
+
+       if (!json_object_object_get_ex(m_json_device_files_obj, DEVICE_TYPE_PROP_PLAYBACK_DEVICES, &playback_devices_o)) {
+               cout << "failed to get playback" << endl;
+               return;
+       }
+
+       cout << "  " << DEVICE_TYPE_PROP_PLAYBACK_DEVICES << " : ["  << endl;
+       parse_device_file_array_object(playback_devices_o, m_playback);
+       cout << "  ]" << endl;
+}
+
+void CDeviceMapParser::parse_capture()
+{
+       json_object *capture_devices_o = NULL;
+
+       if (!json_object_object_get_ex(m_json_device_files_obj, DEVICE_TYPE_PROP_CAPTURE_DEVICES, &capture_devices_o)) {
+               cout << "failed to get capture" << endl;
+               return;
+       }
+
+       cout << "  " << DEVICE_TYPE_PROP_CAPTURE_DEVICES << " : ["  << endl;
+       parse_device_file_array_object(capture_devices_o, m_capture);
+       cout << "  ]" << endl;
+}
+
+void CDeviceMapParser::get_device(string& s, string& card, string& device_num)
+{
+       // eg. alsa:0,0
+       string delimiter = ",";
+       string s1(s.substr(s.find_last_of(':') + 1));
+
+       // eg. 0,0
+       size_t pos = s1.find(delimiter);
+       string token(s1.substr(0, pos));
+       s1.erase(0, pos + delimiter.length());
+
+       card.assign(token);
+       device_num.assign(s1);
+}
+
+void CDeviceMapParser::get_single_param(string& s, int& rate, int& channels)
+{
+       // eg. rate=44100
+       string delimiter = "=";
+       size_t pos = s.find(delimiter);
+       string token = s.substr(0, pos);
+       s.erase(0, pos + delimiter.length());
+
+       if (token.compare("rate") == 0)
+               rate = stoi(s);
+       else if (token.compare("channels") == 0)
+               channels = stoi(s);
+}
+
+void CDeviceMapParser::get_params(string& s, int& rate, int& channels)
+{
+       // eg. rate=44100 channels=1
+       string delimiter = " ";
+       size_t pos = 0;
+       string token;
+       string s1(s);
+
+       while ((pos = s1.find(delimiter)) != string::npos) {
+               token = s1.substr(0, pos);
+               get_single_param(token, rate, channels);
+               s1.erase(0, pos + delimiter.length());
+       }
+       get_single_param(s1, rate, channels);
+}
+
+
+void CDeviceMapParser::dump_devices()
+{
+       string card, device_num;
+       int rate = -1, channels = -1;
+
+       get_playback(card, device_num, rate, channels);
+       get_capture(card, device_num, rate, channels);
+}
+
+void CDeviceMapParser::get_playback(string& card, string& device_num, int& rate, int& channels)
+{
+       parse_playback();
+
+       get_device(m_playback.first, card, device_num);
+       get_params(m_playback.second, rate, channels);
+       cout << "  1. PLAYBACK" << endl;
+       cout << "     > card=" << card << ", device=" << device_num << endl;
+       cout << "     > rate=" << rate << ", channels=" << channels << endl << endl;
+}
+
+void CDeviceMapParser::get_capture(string& card, string& device_num, int& rate, int& channels)
+{
+       parse_capture();
+
+       get_device(m_capture.first, card, device_num);
+       get_params(m_capture.second, rate, channels);
+       cout << "  2. CAPTURE" << endl;
+       cout << "     > card=" << card << ", device=" << device_num << endl;
+       cout << "     > rate=" << rate << ", channels=" << channels << endl;
+}
+
+void CDeviceMapParser::parse_device_string_object(json_object *device_string_o, string& device_string)
+{
+       assert(device_string_o);
+       assert(json_object_is_type(device_string_o, json_type_string));
+
+       // object example
+       //   device-string : alsa:sprdphone,0,
+
+       device_string.assign(json_object_get_string(device_string_o));
+
+       cout << "      " << DEVICE_TYPE_PROP_DEVICE_STRING << " : " << device_string << "," << endl;
+}
+
+void CDeviceMapParser::parse_device_role_object(json_object *device_role_o, string& device_params)
+{
+       struct json_object_iterator it, it_end;
+
+       assert(device_role_o);
+       assert(json_object_is_type(device_role_o, json_type_object));
+
+       // <object example>
+       //   role : { normal : rate=44100 }
+
+       it = json_object_iter_begin(device_role_o);
+       it_end = json_object_iter_end(device_role_o);
+
+       while (!json_object_iter_equal(&it, &it_end)) {
+               if (strcmp(json_object_iter_peek_name(&it), "normal") == 0) {
+                       device_params.assign(json_object_get_string(json_object_iter_peek_value(&it)));
+                       cout << "      " << DEVICE_TYPE_PROP_ROLE << " : {  normal : " << device_params << " }" << endl;
+                       break;
+               }
+
+               json_object_iter_next(&it);
+       }
+}
+
+void CDeviceMapParser::parse_device_file_object(json_object *device_file_o, pair<string, string>& device)
+{
+       json_object *device_file_prop_o = NULL;
+       string device_string, device_param;
+
+       assert(device_file_o);
+       assert(json_object_is_type(device_file_o, json_type_object));
+
+       // <object example>
+       //      device-string : alsa:sprdphone,0,
+       //      role : { normal : rate=44100 }
+
+       // parse role
+       if (!json_object_object_get_ex(device_file_o, DEVICE_TYPE_PROP_ROLE, &device_file_prop_o)) {
+               cout << "Get device role object failed" << endl;
+               return;
+       }
+       parse_device_role_object(device_file_prop_o, device_param);
+
+       if (device_param.empty()) {
+               cout << "      " << "[E] This is not a normal device..skip" << endl;
+               return;
+       }
+
+       // parse device-string
+       if (!json_object_object_get_ex(device_file_o, DEVICE_TYPE_PROP_DEVICE_STRING, &device_file_prop_o)) {
+               cout << "Get device-string object failed" << endl;
+               return;
+       }
+       parse_device_string_object(device_file_prop_o, device_string);
+
+       // store device information
+       device = make_pair(device_string, device_param);
+}
+
+void CDeviceMapParser::parse_device_file_array_object(json_object *device_file_array_o, pair<string, string>& device)
+{
+       int num, idx;
+       json_object *device_file_o = NULL;
+
+       assert(device_file_array_o);
+       assert(json_object_is_type(device_file_array_o, json_type_array));
+
+       // <object example>
+       //  {
+       //        device-string : alsa:sprdphone,0,
+       //        role : { normal : rate=44100 }
+       //  }
+
+       // ToDo : this might be replaced with iterator such as foreach?
+       num = json_object_array_length(device_file_array_o);
+       for (idx = 0; idx < num; idx++) {
+               if (!(device_file_o = json_object_array_get_idx(device_file_array_o, idx))) {
+                       cout << "Get device file object failed" << endl;
+                       return;
+               }
+
+               cout << "    {" << endl;
+               parse_device_file_object(device_file_o, device);
+               cout << "    }" << endl;
+       }
+}
diff --git a/testcase/audio/parser.hh b/testcase/audio/parser.hh
new file mode 100644 (file)
index 0000000..b53ba89
--- /dev/null
@@ -0,0 +1,43 @@
+#include <iostream>
+#include <string>
+#include <json.h>
+#include <assert.h>
+#include <string.h>
+
+using namespace std;
+
+class CDeviceMapParser
+{
+public:
+       CDeviceMapParser();
+       CDeviceMapParser(const char* map_file);
+       virtual ~CDeviceMapParser();
+
+       void dump_devices();
+
+       void get_playback(string& card, string& device_num, int& rate, int& channels);
+       void get_capture(string& card, string& device_num, int& rate, int& channels);
+
+private:
+       void open_json(const char* json_file);
+       void close_json();
+       void parse_device_string_object(json_object *device_string_o, string& device_string);
+       void parse_device_role_object(json_object *device_role_o, string& device_params);
+       void parse_device_file_object(json_object *device_file_o, pair<string, string>& device);
+       void parse_device_file_array_object(json_object *device_file_array_o, pair<string, string>& device);
+
+       void parse_playback();
+       void parse_capture();
+       void get_device(string& s, string& card, string& device_num);
+       void get_params(string& s, int& rate, int& channels);
+       void get_single_param(string& s, int& rate, int& channels);
+
+       // FixMe, pair doens't define what is paired clearly....
+       pair<string, string> m_playback; // device_string, device_params
+       pair<string, string> m_capture;  // device_string, device_params
+
+       json_object *m_json_obj;
+       json_object *m_json_device_files_obj;
+};
+
+