From: Eunhye Choi Date: Tue, 4 Jun 2019 06:56:39 +0000 (+0900) Subject: [ACR-1410] add API to export audio PCM X-Git-Tag: accepted/tizen/unified/20190613.061520^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=0389ae71dd4d09eea681ac4d7cd690b671316baf;p=platform%2Fcore%2Fapi%2Fplayer.git [ACR-1410] add API to export audio PCM - Add API to export audio PCM - Add test code to verify the new API - exchange tv product option in spec file Change-Id: Iae73174cc506ff9064f8a6ed1d9303f9c510c4db --- diff --git a/include/player.h b/include/player.h index dc3850a..55f2a1d 100644 --- a/include/player.h +++ b/include/player.h @@ -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 + #include + ... + 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); diff --git a/packaging/capi-media-player.spec b/packaging/capi-media-player.spec index 81c1d9c..4273bbf 100644 --- a/packaging/capi-media-player.spec +++ b/packaging/capi-media-player.spec @@ -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 diff --git a/src/player.c b/src/player.c index 4e37866..2976bb8 100644 --- a/src/player.c +++ b/src/player.c @@ -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); diff --git a/test/player_test.c b/test/player_test.c index e3118cd..3f2eb64 100644 --- a/test/player_test.c +++ b/test/player_test.c @@ -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);