[ACR-1410] add API to export audio PCM 79/207379/14
authorEunhye Choi <eunhae1.choi@samsung.com>
Tue, 4 Jun 2019 06:56:39 +0000 (15:56 +0900)
committerEunhye Choi <eunhae1.choi@samsung.com>
Wed, 12 Jun 2019 05:22:08 +0000 (14:22 +0900)
- Add API to export audio PCM
- Add test code to verify the new API
- exchange tv product option in spec file

Change-Id: Iae73174cc506ff9064f8a6ed1d9303f9c510c4db

include/player.h
packaging/capi-media-player.spec
src/player.c
test/player_test.c

index dc3850a..55f2a1d 100644 (file)
@@ -148,6 +148,18 @@ typedef enum {
 } player_media_stream_buffer_status_e;
 
 /**
+ * @brief Enumeration of audio extract option.
+ * @since_tizen 5.5
+ * @see player_set_media_packet_audio_frame_decoded_cb()
+ */
+typedef enum {
+       PLAYER_AUDIO_EXTRACT_DEFAULT                        = 0x00,   /**< Sync with the playback clock and multichannel audio stream */
+       PLAYER_AUDIO_EXTRACT_NO_SYNC_WITH_CLOCK             = 0x01,   /**< No sync with the playback clock */
+       PLAYER_AUDIO_EXTRACT_DEINTERLEAVE                   = 0x02,   /**< Splits one interleaved multichannel audio stream into many mono audio streams */
+       PLAYER_AUDIO_EXTRACT_NO_SYNC_AND_DEINTERLEAVE       = 0x03,   /**< No sync with clock and splits into mono streams */
+} player_audio_extract_option_e;
+
+/**
  * @}
  */
 
@@ -320,7 +332,7 @@ typedef void (*player_error_cb)(int error_code, void *user_data);
 /**
  * @brief Called when the video is captured.
  * @since_tizen @if WEARABLE 2.3.1 @else 2.3 @endif
- * @remarks The color space format of the captured image is IMAGE_UTIL_COLORSPACE_RGB888.
+ * @remarks The color space format of the captured image is #IMAGE_UTIL_COLORSPACE_RGB888.
  * @remarks The @a captured_data should not be released and it can be used only in the callback.
  *          To use outside, make a copy.
  * @param[in] captured_data     The captured image buffer
@@ -333,7 +345,7 @@ typedef void (*player_error_cb)(int error_code, void *user_data);
 typedef void (*player_video_captured_cb)(unsigned char *captured_data, int width, int height, unsigned int size, void *user_data);
 
 /**
- * @brief Called to register for notifications about delivering media packet when every video frame is decoded.
+ * @brief Called to register for notifications about delivering media packet when each video frame is decoded.
  * @since_tizen @if WEARABLE 2.3.1 @else 2.3 @endif
  * @remarks The UI update code must not be directly invoked.\n
  * @remarks The @a packet should be released using media_packet_destroy(). \n
@@ -345,6 +357,17 @@ typedef void (*player_video_captured_cb)(unsigned char *captured_data, int width
 typedef void (*player_media_packet_video_decoded_cb)(media_packet_h packet, void *user_data);
 
 /**
+ * @brief Called to register for notifications about delivering media packet when audio frame is decoded.
+ * @since_tizen 5.5
+ * @remarks The @a packet should be released by calling media_packet_destroy(). \n
+ *          It is recommended to release it as soon as it is rendered, to avoid memory exhaustion.
+ *
+ * @param[in] packet      Reference pointer to the media packet
+ * @param[in] user_data   The user data passed from the callback registration function
+ */
+typedef void (*player_media_packet_audio_decoded_cb)(media_packet_h packet, void *user_data);
+
+/**
  * @brief Called when the buffer level drops below the threshold of max size or no free space in buffer.
  * @since_tizen @if WEARABLE 3.0 @else 2.4 @endif
  * @remarks This function is used for media stream playback only.
@@ -1039,6 +1062,68 @@ int player_set_media_packet_video_frame_decoded_cb(player_h player, player_media
 int player_unset_media_packet_video_frame_decoded_cb(player_h player);
 
 /**
+ * @brief Sets a media packet audio decoded callback function.
+ * @details This function is used to get audio PCM data of input media content via registered callback.\n
+ *          This function allows to specify the output PCM format by @ref CAPI_MEDIA_TOOL_MEDIA_FORMAT_MODULE API.
+ * @since_tizen 5.5
+ * @remarks This function must be called before calling player_prepare() or player_prepare_async().\n
+ *          A registered callback is called in a separate thread (not in the main loop).\n
+ *          The audio PCM data can be retrieved using a registered callback as a media packet
+ *          and it is available until it's destroyed by media_packet_destroy().\n
+ *          The packet has to be destroyed as quickly as possible after rendering the data\n
+ *          and all the packets have to be destroyed before player_unprepare() is called.\n
+ * @param[in] player     The handle to the media player
+ * @param[in] format     The media format handle about required audio PCM specification.
+ *                       This format has to include PCM MIME type, audio channel and sampling rate.
+ *                       If the format is NULL, the original PCM format or platform default PCM format will be applied.
+ * @param[in] opt        The audio extract option
+ * @param[in] callback   The callback function to be registered
+ * @param[in] user_data  The user data to be passed to the callback function
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #PLAYER_ERROR_NONE Successful
+ * @retval #PLAYER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #PLAYER_ERROR_INVALID_STATE Invalid state
+ * @retval #PLAYER_ERROR_INVALID_OPERATION Invalid operation
+ * @pre The player's state must be #PLAYER_STATE_IDLE.
+ * @see player_unset_media_packet_audio_frame_decoded_cb()
+ * @par Example
+   @code
+      #include <player.h>
+      #include <media_format.h>
+       ...
+       player_h player = NULL;
+       media_format_h a_format = NULL;
+       ...
+       media_format_create(&a_format);
+       media_format_set_audio_mime(a_format, MEDIA_FORMAT_PCM_F32LE);
+       media_format_set_audio_channel(a_format, 2);
+       media_format_set_audio_samplerate(a_format, 44100);
+
+       player_set_media_packet_audio_frame_decoded_cb(player, a_format, PLAYER_AUDIO_EXTRACT_DEFAULT, _audio_pcm_cb, udata);
+
+       media_format_unref(a_format);
+       ...
+   @endcode
+ */
+int player_set_media_packet_audio_frame_decoded_cb(player_h player, media_format_h format,
+               player_audio_extract_option_e opt, player_media_packet_audio_decoded_cb callback, void *user_data);
+
+/**
+ * @brief Unsets the media packet audio frame decoded callback function.
+ * @since_tizen 5.5
+ * @param[in] player The handle to the media player
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #PLAYER_ERROR_NONE Successful
+ * @retval #PLAYER_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #PLAYER_ERROR_INVALID_STATE Invalid state
+ * @pre The player's state must be #PLAYER_STATE_READY or #PLAYER_STATE_IDLE
+ * @see player_set_media_packet_audio_frame_decoded_cb()
+ */
+int player_unset_media_packet_audio_frame_decoded_cb(player_h player);
+
+/**
  * @brief  Pushes elementary stream to decode audio or video.
  * @since_tizen @if WEARABLE 3.0 @else 2.4 @endif
  * @remarks player_set_media_stream_info() must be called before using this function.
@@ -1070,7 +1155,7 @@ int player_push_media_stream(player_h player, media_packet_h packet);
  * @remarks H.264 can be supported.
  * @param[in] player The handle to media player
  * @param[in] type   The type of target stream
- * @param[in] format The media format to set audio information
+ * @param[in] format The media format to set media information
  * @return @c 0 on success,
  *         otherwise a negative error value
  * @retval #PLAYER_ERROR_NONE Successful
@@ -2265,7 +2350,7 @@ int player_set_replaygain_enabled(player_h player, bool enabled);
  * @retval #PLAYER_ERROR_NONE Successful
  * @retval #PLAYER_ERROR_INVALID_PARAMETER Invalid parameter
  * @retval #PLAYER_ERROR_INVALID_OPERATION Invalid operation
- * @see player_set_replaygain_enable()
+ * @see player_set_replaygain_enabled()
  */
 int player_is_replaygain_enabled(player_h player, bool *enabled);
 
index 81c1d9c..4273bbf 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-player
 Summary:    A Media Player API
-Version:    0.3.116
+Version:    0.3.117
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
@@ -27,7 +27,7 @@ BuildRequires:  pkgconfig(eom)
 BuildRequires:  pkgconfig(storage)
 BuildRequires:  pkgconfig(capi-system-info)
 BuildRequires:  pkgconfig(libinput)
-%if "%{TIZEN_PRODUCT_TV}" != "1"
+%if "%{tizen_profile_name}" != "tv"
 BuildRequires:  pkgconfig(mm-evas-renderer)
 %endif
 
@@ -82,7 +82,7 @@ export LDFLAGS+=" -lgcov"
 
 MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'`
 %cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} -DFULLVER=%{version} -DMAJORVER=${MAJORVER} \
-%if "%{TIZEN_PRODUCT_TV}" == "1"
+%if "%{tizen_profile_name}" == "tv"
        -DTIZEN_FEATURE_EVAS_RENDERER=Off
 %else
        -DTIZEN_FEATURE_EVAS_RENDERER=On
index 4e37866..2976bb8 100644 (file)
@@ -67,7 +67,14 @@ typedef struct {
        gint fd;
        gint fd_id;
        bool use_tsurf_pool;
-} _media_pkt_fin_data;
+} _media_pkt_video_fin_data;
+
+typedef struct {
+       gint key;
+       gint fd;
+       gint fd_id;
+       tbm_bo bo;
+} _media_pkt_audio_fin_data;
 
 static int _player_deinit_memory_buffer(player_cli_s *pc);
 static void _player_event_queue_add(player_event_queue *ev, _player_cb_data *data);
@@ -209,11 +216,11 @@ static bool _player_get_param_value(char *buf, ...)
        return ret;
 }
 
-int _player_media_packet_finalize(media_packet_h pkt, int error_code, void *user_data)
+int _player_video_media_packet_finalize(media_packet_h pkt, int error_code, void *user_data)
 {
        int ret = MEDIA_PACKET_FINALIZE;
        muse_player_api_e api = MUSE_PLAYER_API_RETURN_VIDEO_DATA;
-       _media_pkt_fin_data *fin_data = (_media_pkt_fin_data *)user_data;
+       _media_pkt_video_fin_data *fin_data = (_media_pkt_video_fin_data *)user_data;
        intptr_t v_data = 0;
        char *snd_msg = NULL;
        int snd_len = 0;
@@ -235,7 +242,7 @@ int _player_media_packet_finalize(media_packet_h pkt, int error_code, void *user
                        /* continue the remained job */
                }
                if (tsurf) {
-                       /* LOGD("_player_media_packet_finalize tsurf destroy %p", tsurf); */
+                       /* LOGD("tsurf destroy %p", tsurf); */
                        tbm_surface_destroy(tsurf);
                        tsurf = NULL;
                }
@@ -278,6 +285,47 @@ EXIT:
        return ret;
 }
 
+int _player_audio_media_packet_finalize(media_packet_h pkt, int error_code, void *user_data)
+{
+       int ret = MEDIA_PACKET_FINALIZE;
+       muse_player_api_e api = MUSE_PLAYER_API_RETURN_BUFFER;
+       _media_pkt_audio_fin_data *fin_data = (_media_pkt_audio_fin_data *)user_data;
+
+       if (!pkt) {
+               LOGE("invalid parameter buffer %p, user_data %p", pkt, user_data);
+               return ret;
+       }
+
+       if (!fin_data) {
+               LOGE("invalid fin_data");
+               goto EXIT;
+       }
+
+       if (fin_data->bo) {
+               /* LOGD("release memory - %p", fin_data->bo); */
+               tbm_bo_unref(fin_data->bo);
+       }
+
+       if (fin_data->fd > INVALID_DEFAULT_VALUE && muse_core_fd_is_valid(fin_data->fd)) {
+               if (muse_client_check_fd_id_value(fin_data->fd, fin_data->fd_id) == FALSE) {
+                       LOGE("[fd:%d,id:%d] is invalid.", fin_data->fd, fin_data->fd_id);
+                       goto EXIT;
+               }
+
+               PLAYER_SEND_MSG_ASYNC_WITH_NO_RETURN(api, fin_data->fd, MUSE_TYPE_INT, "key", fin_data->key);
+       } else {
+               LOGE("[fd:%d] is invalid.", fin_data->fd);
+       }
+
+EXIT:
+       if (fin_data) {
+               g_free(fin_data);
+               fin_data = NULL;
+       }
+
+       return ret;
+}
+
 static bool _player_video_roi_area_is_valid(double x_scale, double y_scale,
        double w_scale, double h_scale)
 {
@@ -494,6 +542,23 @@ static int __unset_callback(muse_player_event_e type, player_h player)
        LOGI("Event type : %d ", type);
 
        PLAYER_NULL_ARG_CHECK(CALLBACK_INFO(pc));
+
+       if ((type == MUSE_PLAYER_EVENT_TYPE_MEDIA_PACKET_VIDEO_FRAME) ||
+               (type == MUSE_PLAYER_EVENT_TYPE_MEDIA_PACKET_AUDIO_FRAME)) {
+               /* check state condition */
+               player_state_e state = PLAYER_STATE_NONE;
+
+               if (_get_current_state(pc, &state) != PLAYER_ERROR_NONE) {
+                       LOGE("Failed to get current state");
+                       return PLAYER_ERROR_INVALID_OPERATION;
+               }
+
+               if (state > PLAYER_STATE_READY) {
+                       LOGE("Invalid state %d", state);
+                       return PLAYER_ERROR_INVALID_STATE;
+               }
+       }
+
        set_null_user_cb_lock(CALLBACK_INFO(pc), type);
 
        PLAYER_SEND_MSG(api, pc, ret_buf, ret,
@@ -789,7 +854,7 @@ static void __media_packet_video_frame_cb_handler(callback_cb_info_s *cb_info, _
        media_format_mimetype_e mimetype = MEDIA_FORMAT_NV12;
        bool make_pkt_fmt = false;
        int ret = MEDIA_FORMAT_ERROR_NONE;
-       _media_pkt_fin_data *fin_data = NULL;
+       _media_pkt_video_fin_data *fin_data = NULL;
        intptr_t v_data = 0;
        uint64_t pts = 0;
        int i = 0, orientation = 0;
@@ -904,7 +969,7 @@ static void __media_packet_video_frame_cb_handler(callback_cb_info_s *cb_info, _
                }
        }
 
-       fin_data = g_new0(_media_pkt_fin_data, 1);
+       fin_data = g_new0(_media_pkt_video_fin_data, 1);
        if (!fin_data) {
                LOGE("failed to alloc fin_data");
                goto ERROR;
@@ -916,7 +981,7 @@ static void __media_packet_video_frame_cb_handler(callback_cb_info_s *cb_info, _
        /* Keep the fd id to check validation when the pkt is destroyed. */
        fin_data->fd_id = muse_client_get_fd_id_value(fin_data->fd);
 
-       ret = media_packet_create_from_tbm_surface(cb_info->pkt_fmt, tsurf, (media_packet_finalize_cb)_player_media_packet_finalize, (void *)fin_data, &pkt);
+       ret = media_packet_create_from_tbm_surface(cb_info->pkt_fmt, tsurf, (media_packet_finalize_cb)_player_video_media_packet_finalize, (void *)fin_data, &pkt);
        if (ret != MEDIA_PACKET_ERROR_NONE || !pkt) {
                LOGE("media_packet_create_from_tbm_surface failed %d %p", ret, pkt);
                goto ERROR;
@@ -979,10 +1044,14 @@ ERROR:
 
 static void __media_packet_audio_frame_cb_handler(callback_cb_info_s *cb_info, _player_recv_data *recv_data)
 {
+       int ret = MEDIA_FORMAT_ERROR_NONE;
        tbm_bo bo = NULL;
        tbm_bo_handle thandle;
        int key = INVALID_DEFAULT_VALUE;
        player_audio_raw_data_s audio; /* DEPRECATED_PLAYER_INTERNAL_API */
+       media_packet_h pkt = NULL;
+       media_format_h fmt = NULL;
+       _media_pkt_audio_fin_data *fin_data = NULL;
 
        if (!player_msg_get(key, recv_data->buffer)) {
                LOGE("failed to get key value from msg.");
@@ -1011,17 +1080,55 @@ static void __media_packet_audio_frame_cb_handler(callback_cb_info_s *cb_info, _
        tbm_bo_unmap(bo);
 
        /* LOGD("user callback data %p, size %d", audio.data, audio.size); */
-       if (cb_info->user_cb[MUSE_PLAYER_EVENT_TYPE_AUDIO_FRAME]) { /* DEPRECATED_PLAYER_INTERNAL_API, will be removed */
-               ((player_audio_pcm_extraction_cb)cb_info->user_cb[MUSE_PLAYER_EVENT_TYPE_AUDIO_FRAME])
-                                       (&audio, cb_info->user_data[MUSE_PLAYER_EVENT_TYPE_AUDIO_FRAME]);
-       }
-/* FIXME: Will be implemented with public api
-       else if (cb_info->user_cb[MUSE_PLAYER_EVENT_TYPE_MEDIA_PACKET_AUDIO_FRAME]) {
+       if (cb_info->user_cb[MUSE_PLAYER_EVENT_TYPE_MEDIA_PACKET_AUDIO_FRAME]) {
+               ret = media_format_create(&fmt);
+               if (ret != MEDIA_FORMAT_ERROR_NONE || !fmt) {
+                       LOGE("failed to create media format 0x%X %p", ret, fmt);
+                       goto EXIT;
+               }
+
+               ret = media_format_set_audio_mime(fmt, audio.pcm_format);
+               ret |= media_format_set_audio_samplerate(fmt, audio.rate);
+               ret |= media_format_set_audio_channel(fmt, audio.channel);
+               ret |= media_format_set_audio_channel_mask(fmt, audio.channel_mask);
+               if (ret != MEDIA_FORMAT_ERROR_NONE) {
+                       LOGE("failed to set audio format 0x%X", ret);
+                       goto EXIT;
+               }
+
+               fin_data = g_new0(_media_pkt_audio_fin_data, 1);
+               if (!fin_data) {
+                       LOGE("failed to alloc fin_data");
+                       goto EXIT;
+               }
+
+               fin_data->key = key;
+               fin_data->fd = cb_info->fd;
+               fin_data->bo = bo;
+
+               /* Keep the fd id to check validation when the pkt is destroyed. */
+               fin_data->fd_id = muse_client_get_fd_id_value(fin_data->fd);
+
+               ret = media_packet_create_from_external_memory(fmt, audio.data, audio.size,
+                               (media_packet_finalize_cb)_player_audio_media_packet_finalize, (void *)fin_data, &pkt);
+               if (ret != MEDIA_PACKET_ERROR_NONE || !pkt) {
+                       LOGE("failed to create media packet 0x%X %p", ret, pkt);
+                       if (pkt)
+                               media_packet_destroy(pkt); /* fin_data will be free in finalize function. */
+                       else
+                               g_free(fin_data);
+                       goto EXIT;
+               }
+
+               media_format_unref(fmt);
+               fmt = NULL;
+
                ((player_media_packet_audio_decoded_cb)cb_info->user_cb[MUSE_PLAYER_EVENT_TYPE_MEDIA_PACKET_AUDIO_FRAME])
                                        (pkt, cb_info->user_data[MUSE_PLAYER_EVENT_TYPE_MEDIA_PACKET_AUDIO_FRAME]);
-       }
-*/
-       else {
+       } else if (cb_info->user_cb[MUSE_PLAYER_EVENT_TYPE_AUDIO_FRAME]) { /* DEPRECATED_PLAYER_INTERNAL_API, will be removed */
+               ((player_audio_pcm_extraction_cb)cb_info->user_cb[MUSE_PLAYER_EVENT_TYPE_AUDIO_FRAME])
+                                       (&audio, cb_info->user_data[MUSE_PLAYER_EVENT_TYPE_AUDIO_FRAME]);
+       } else {
                LOGE("there is no registered cb");
                goto EXIT;
        }
@@ -1031,7 +1138,10 @@ EXIT:
                close(recv_data->tfd[0]);
        memset(recv_data->tfd, INVALID_DEFAULT_VALUE, sizeof(recv_data->tfd));
 
-       /* return buffer directly, DEPRECATED_PLAYER_INTERNAL_API */
+       if (fmt)
+               media_format_unref(fmt);
+
+       /* return buffer */
        if ((cb_info->user_cb[MUSE_PLAYER_EVENT_TYPE_MEDIA_PACKET_AUDIO_FRAME] == NULL) &&
                (key > INVALID_DEFAULT_VALUE)) {
 
@@ -1040,6 +1150,8 @@ EXIT:
                /* LOGD("send msg to release buffer. key:%d", key); */
                PLAYER_SEND_MSG_ASYNC_WITH_NO_RETURN(MUSE_PLAYER_API_RETURN_BUFFER, cb_info->fd, MUSE_TYPE_INT, "key", key);
        }
+
+       return;
 }
 
 static void __video_frame_render_error_cb_handler(callback_cb_info_s *cb_info, _player_recv_data *recv_data)
@@ -1207,10 +1319,12 @@ gboolean _player_event_job_function(void *user_data)
                return FALSE;
        }
 
-       LOGD("enter ev:%d", data->int_data);
+       ev = data->int_data;
+       if ((ev != MUSE_PLAYER_EVENT_TYPE_MEDIA_PACKET_VIDEO_FRAME) &&
+               (ev != MUSE_PLAYER_EVENT_TYPE_MEDIA_PACKET_AUDIO_FRAME))
+               LOGD("enter ev:%d", ev);
 
        g_mutex_lock(&data->event_mutex);
-       ev = data->int_data;
 
        if (data->cb_info == NULL) {
                /* tried to remove before at _player_remove_idle_event */
@@ -4045,6 +4159,59 @@ int player_unset_media_packet_video_frame_decoded_cb(player_h player)
        return __unset_callback(MUSE_PLAYER_EVENT_TYPE_MEDIA_PACKET_VIDEO_FRAME, player);
 }
 
+int player_set_media_packet_audio_frame_decoded_cb(player_h player, media_format_h format,
+               player_audio_extract_option_e opt, player_media_packet_audio_decoded_cb callback, void *user_data)
+{
+       PLAYER_INSTANCE_CHECK(player);
+       PLAYER_NULL_ARG_CHECK(callback);
+       int ret = PLAYER_ERROR_NONE;
+       player_cli_s *pc = (player_cli_s *)player;
+       muse_player_api_e api = MUSE_PLAYER_API_SET_MEDIA_PACKET_AUDIO_FRAME_DECODED_CB;
+       char *ret_buf = NULL;
+       media_format_mimetype_e mimetype = MEDIA_FORMAT_MAX;
+       int channel = 0;
+       int samplerate = 0;
+
+       LOGD("ENTER");
+
+       if (format) {
+               media_format_ref(format);
+               if (media_format_get_audio_info(format, &mimetype, &channel, &samplerate, NULL, NULL) != MEDIA_FORMAT_ERROR_NONE) {
+                       LOGE("failed to get audio info from media format.");
+                       media_format_unref(format);
+                       return PLAYER_ERROR_INVALID_PARAMETER;
+               }
+               media_format_unref(format);
+
+               if (mimetype < MEDIA_FORMAT_PCM || mimetype > MEDIA_FORMAT_PCM_U32BE) {
+                       LOGW("Not supported audio format type : 0x%X", mimetype);
+                       return PLAYER_ERROR_INVALID_PARAMETER;
+               }
+       }
+
+       LOGD("pcm spec : 0x%X, %d, %d", mimetype, channel, samplerate);
+
+       PLAYER_SEND_MSG(api, pc, ret_buf, ret,
+                                       MUSE_TYPE_INT, "opt", opt,
+                                       MUSE_TYPE_INT, "mimetype", mimetype,
+                                       MUSE_TYPE_INT, "channel", channel,
+                                       MUSE_TYPE_INT, "samplerate", samplerate);
+
+       if (ret == PLAYER_ERROR_NONE) {
+               pc->cb_info->user_cb[MUSE_PLAYER_EVENT_TYPE_MEDIA_PACKET_AUDIO_FRAME] = callback;
+               pc->cb_info->user_data[MUSE_PLAYER_EVENT_TYPE_MEDIA_PACKET_AUDIO_FRAME] = user_data;
+               LOGI("Event type : %d ", MUSE_PLAYER_EVENT_TYPE_MEDIA_PACKET_AUDIO_FRAME);
+       }
+
+       g_free(ret_buf);
+       return ret;
+}
+
+int player_unset_media_packet_audio_frame_decoded_cb(player_h player)
+{
+       return __unset_callback(MUSE_PLAYER_EVENT_TYPE_MEDIA_PACKET_AUDIO_FRAME, player);
+}
+
 int player_set_video_stream_changed_cb(player_h player, player_video_stream_changed_cb callback, void *user_data)
 {
        return __set_callback(MUSE_PLAYER_EVENT_TYPE_VIDEO_STREAM_CHANGED, player, callback, user_data);
index e3118cd..3f2eb64 100644 (file)
@@ -57,7 +57,12 @@ typedef enum {
        TIZEN_PROFILE_TV = 0x4,
        TIZEN_PROFILE_IVI = 0x8,
        TIZEN_PROFILE_COMMON = 0x10,
-} tizen_profile_t;
+} tizen_profile_e;
+
+typedef struct {
+       uint64_t mask;
+       FILE *fp_out;
+} audio_pcm_dump_t;
 
 extern int player_set_audio_offload_enabled(player_h player, bool enabled);
 extern int player_is_audio_offload_enabled(player_h player, bool *enabled);
@@ -70,10 +75,10 @@ mm_navevent_handler_h event_handler;
 mm_navevent_handler_size_s image_size;
 #endif
 
-static tizen_profile_t _get_tizen_profile()
+static tizen_profile_e _get_tizen_profile()
 {
        char *profileName;
-       static tizen_profile_t profile = TIZEN_PROFILE_UNKNOWN;
+       static tizen_profile_e profile = TIZEN_PROFILE_UNKNOWN;
 
        if (__builtin_expect(profile != TIZEN_PROFILE_UNKNOWN, 1))
                return profile;
@@ -125,6 +130,7 @@ static int _save(unsigned char *src, int length);
 #if DUMP_OUTBUF
 FILE *fp_out1 = NULL;
 FILE *fp_out2 = NULL;
+GList *audio_dump_list = NULL;
 #endif
 
 enum {
@@ -173,6 +179,10 @@ enum {
        CURRENT_STATUS_AUDIO_OFFLOAD,
        CURRENT_STATUS_PITCH_CONTROL,
        CURRENT_STATUS_PITCH_VALUE,
+       CURRENT_STATUS_EXPORT_PCM_OPTION,
+       CURRENT_STATUS_EXPORT_PCM_CH,
+       CURRENT_STATUS_EXPORT_PCM_RATE,
+       CURRENT_STATUS_EXPORT_PCM_MIME,
 };
 
 #define MAX_HANDLE 20
@@ -1183,6 +1193,20 @@ static void _player_destroy()
                media_packet_destroy(g_audio_pkt);
 
 #if DUMP_OUTBUF
+       GList *list = NULL;
+       audio_pcm_dump_t *a_data = NULL;
+
+       list = audio_dump_list;
+       while(list) {
+               a_data = (audio_pcm_dump_t *)list->data;
+               list = g_list_next(list);
+               if (a_data && a_data->fp_out)
+                               fclose(a_data->fp_out);
+               g_free(a_data);
+       }
+       g_list_free(audio_dump_list);
+       audio_dump_list = NULL;
+
        if (fp_out1)
                fclose(fp_out1);
        if (fp_out2)
@@ -1637,6 +1661,135 @@ static void set_video_frame_decoded_cb(void)
        player_set_media_packet_video_frame_decoded_cb(g_player[0], _video_decoded_cb, g_player[0]);
 }
 
+void _audio_decoded_cb(media_packet_h packet, void *user_data)
+{
+       int mime, channel, rate;
+       uint64_t channel_mask;
+       uint64_t size;
+       void *pkt_data;
+
+       if (!packet) {
+               g_print("invalid packet param");
+               return;
+       }
+
+       media_packet_get_format(packet, &g_audio_fmt);
+       media_format_get_audio_info(g_audio_fmt, (media_format_mimetype_e *)&mime, &channel, &rate, NULL, NULL);
+       media_format_get_audio_channel_mask(g_audio_fmt, &channel_mask);
+       media_packet_get_buffer_data_ptr(packet, &pkt_data);
+       media_packet_get_buffer_size(packet, &size);
+
+       g_print("[ received ] channel: %d size: %"G_GUINT64_FORMAT", mask %"G_GUINT64_FORMAT"\n", channel, size, channel_mask);
+
+#if DUMP_OUTBUF
+       GList *list = NULL;
+       audio_pcm_dump_t *a_data = NULL;
+       FILE *fp_dump = NULL;
+       char file_path[32];
+
+       list = audio_dump_list;
+       while(list) {
+               a_data = (audio_pcm_dump_t *)list->data;
+               list = g_list_next(list);
+
+               if (a_data && a_data->mask == channel_mask) {
+                       fp_dump = a_data->fp_out;
+                       break;
+               }
+       }
+
+       a_data = NULL;
+       if (!fp_dump) {
+               a_data = g_try_malloc(sizeof(audio_pcm_dump_t));
+               if (a_data == NULL) {
+                       g_print("failed to malloc");
+                       goto EXIT;
+               }
+
+               snprintf(file_path, 32, "/tmp/out%"G_GUINT64_FORMAT".pcm", channel_mask);
+
+               a_data->mask = channel_mask;
+               a_data->fp_out = fopen(file_path, "wb");
+               if (!a_data->fp_out) {
+                       g_free(a_data);
+                       g_print("[ERROR] failed to open file\n");
+                       goto EXIT;
+               }
+
+               audio_dump_list = g_list_append(audio_dump_list, a_data);
+               fp_dump = a_data->fp_out;
+       }
+
+       fwrite((guint8 *)pkt_data, 1, size, fp_dump);
+#endif
+
+EXIT:
+       media_packet_destroy(packet);
+}
+
+static media_format_mimetype_e __convert_audio_pcm_str_to_media_format_mime(char *audio_pcm_str)
+{
+       int len = strlen("S16LE");
+       if (!audio_pcm_str) {
+               g_print("audio pcm str is NULL\n");
+               return MEDIA_FORMAT_MAX;
+       }
+
+       if (!strncasecmp(audio_pcm_str, "S16LE", len))
+               return MEDIA_FORMAT_PCM_S16LE;
+       else if (!strncasecmp(audio_pcm_str, "S24LE", len))
+               return MEDIA_FORMAT_PCM_S24LE;
+       else if (!strncasecmp(audio_pcm_str, "S32LE", len))
+               return MEDIA_FORMAT_PCM_S32LE;
+       else if (!strncasecmp(audio_pcm_str, "S16BE", len))
+               return MEDIA_FORMAT_PCM_S16BE;
+       else if (!strncasecmp(audio_pcm_str, "S24BE", len))
+               return MEDIA_FORMAT_PCM_S24BE;
+       else if (!strncasecmp(audio_pcm_str, "S32BE", len))
+               return MEDIA_FORMAT_PCM_S32BE;
+       else if (!strncasecmp(audio_pcm_str, "F32LE", len))
+               return MEDIA_FORMAT_PCM_F32LE;
+       else if (!strncasecmp(audio_pcm_str, "F32BE", len))
+               return MEDIA_FORMAT_PCM_F32BE;
+       else if (!strncasecmp(audio_pcm_str, "U16LE", len))
+               return MEDIA_FORMAT_PCM_U16LE;
+       else if (!strncasecmp(audio_pcm_str, "U24LE", len))
+               return MEDIA_FORMAT_PCM_U24LE;
+       else if (!strncasecmp(audio_pcm_str, "U32LE", len))
+               return MEDIA_FORMAT_PCM_U32LE;
+       else if (!strncasecmp(audio_pcm_str, "U16BE", len))
+               return MEDIA_FORMAT_PCM_U16BE;
+       else if (!strncasecmp(audio_pcm_str, "U24BE", len))
+               return MEDIA_FORMAT_PCM_U24BE;
+       else if (!strncasecmp(audio_pcm_str, "U32BE", len))
+               return MEDIA_FORMAT_PCM_U32BE;
+       else {
+               g_print("Not supported audio pcm format str : %s\n", audio_pcm_str);
+               return MEDIA_FORMAT_MAX;
+       }
+}
+
+static void set_audio_frame_decoded_cb(player_audio_extract_option_e opt, char* mime, int ch, int rate)
+{
+       media_format_mimetype_e type = __convert_audio_pcm_str_to_media_format_mime(mime);
+
+       if (type == MEDIA_FORMAT_MAX) {
+               g_print("failed to convert the mime : %s\n", mime);
+               return;
+       }
+
+       media_format_create(&g_audio_fmt);
+       media_format_set_audio_mime(g_audio_fmt, type);
+       media_format_set_audio_channel(g_audio_fmt, ch);
+       media_format_set_audio_samplerate(g_audio_fmt, rate);
+
+       g_print("==========> [Player_Test] option = 0x%X, %s(%d), %d, %d\n", opt, mime, type, ch, rate);
+       player_set_media_packet_audio_frame_decoded_cb(g_player[0], g_audio_fmt, opt, _audio_decoded_cb, g_player[0]);
+
+       media_format_unref(g_audio_fmt);
+       g_audio_fmt = NULL;
+}
+
 static void change_surface(int option)
 {
        player_display_type_e surface_type = 0;
@@ -2262,6 +2415,8 @@ void _interpret_main_menu(char *cmd)
                        audio_frame_decoded_cb_ex(TRUE);
                } else if (strncmp(cmd, "X4", 2) == 0) {
                        audio_frame_decoded_cb_ex(FALSE);
+               } else if (strncmp(cmd, "X5", 2) == 0) {
+                       g_menu_state = CURRENT_STATUS_EXPORT_PCM_OPTION;
                } else if (strncmp(cmd, "ep", 2) == 0) {
                        _player_enable_tsurf_pool();
                } else if (strncmp(cmd, "su", 2) == 0) {
@@ -2380,7 +2535,8 @@ void display_sub_basic()
        g_print("gu. get next uri. \t");
        g_print("sg. set gapless. \n");
        g_print("[audio_frame_decoded_cb_ex] X3. set audio_cb with sync\t");
-       g_print("X4. set audio_cb with async \n");
+       g_print("X4. set audio_cb with async\t");
+       g_print("X5. set audio decoded cb with option\n");
        g_print("[video_frame_decoded_cb] ep. enable tbm surface pool\n");
        g_print("[buffering] bf. set new buffering size\n");
        g_print("[Video 360] si. check spherical info\t");
@@ -2491,6 +2647,14 @@ static void displaymenu()
                g_print("*** input pitch control value.(0:disable, 1: enable) \n");
        } else if (g_menu_state == CURRENT_STATUS_PITCH_VALUE) {
                g_print("*** input pitch value.(0.5 ~ 2) \n");
+       } else if (g_menu_state == CURRENT_STATUS_EXPORT_PCM_OPTION) {
+               g_print("*** set option (0~3) \n");
+       } else if (g_menu_state == CURRENT_STATUS_EXPORT_PCM_CH) {
+               g_print("*** set pcm channel (e.g. 2)\n");
+       } else if (g_menu_state == CURRENT_STATUS_EXPORT_PCM_RATE) {
+               g_print("*** set pcm samplerate (e.g. 44100)\n");
+       } else if (g_menu_state == CURRENT_STATUS_EXPORT_PCM_MIME) {
+               g_print("*** set pcm mime (e.g. S16LE) \n");
        } else {
                g_print("*** unknown status.\n");
                quit_program();
@@ -2857,6 +3021,30 @@ static void interpret(char *cmd)
                        reset_menu_state();
                }
                break;
+       case CURRENT_STATUS_EXPORT_PCM_OPTION:
+               {
+                       value1 = atoi(cmd);
+                       g_menu_state = CURRENT_STATUS_EXPORT_PCM_CH;
+               }
+               break;
+       case CURRENT_STATUS_EXPORT_PCM_CH:
+               {
+                       value2 = atoi(cmd);
+                       g_menu_state = CURRENT_STATUS_EXPORT_PCM_RATE;
+               }
+               break;
+       case CURRENT_STATUS_EXPORT_PCM_RATE:
+               {
+                       value3 = atoi(cmd);
+                       g_menu_state = CURRENT_STATUS_EXPORT_PCM_MIME;
+               }
+               break;
+       case CURRENT_STATUS_EXPORT_PCM_MIME:
+               {
+                       set_audio_frame_decoded_cb((player_audio_extract_option_e)value1, cmd, value2, value3);
+                       reset_menu_state();
+               }
+               break;
        }
 
        g_timeout_add(100, timeout_menu_display, 0);