From 8cd6ac3bae3c98b1e7296e23d2abf4520e6bb29e Mon Sep 17 00:00:00 2001 From: Sangchul Lee Date: Fri, 2 Feb 2018 12:56:27 +0900 Subject: [PATCH] Add support for pcm dump original patches : 9cc8f487ce077b3edfd3ae169df9c8b793d4dc9c - jungsup4.lee@samsung.com - 2017-05-11 - sink-input: Enhance PCM dump : d2f6895a10071acd1929d6fa7a6c4bfbefb2d39b - hs7388.lee@samsung.com - 2017-03-28 - Enhance log format for dlog & enhance pcm dump, remove unused code [Version] 11.1-6 [Issue Type] feature Change-Id: I71c60a7be35da26ddf80779d986cd744aca91b4f Signed-off-by: Sangchul Lee --- configure.ac | 13 +++++ packaging/pulseaudio.spec | 3 +- pulsecore.pc.in | 2 +- src/Makefile.am | 15 ++++++ src/map-file | 2 + src/pulse/introspect.c | 46 ++++++++++++++++ src/pulse/introspect.h | 5 ++ src/pulsecore/core.c | 4 ++ src/pulsecore/core.h | 29 ++++++++++ src/pulsecore/native-common.h | 5 ++ src/pulsecore/protocol-native.c | 57 +++++++++++++++++++- src/pulsecore/sink-input.c | 116 ++++++++++++++++++++++++++++++++++++++++ src/pulsecore/sink-input.h | 5 ++ src/pulsecore/sink.c | 100 ++++++++++++++++++++++++++++++++++ src/pulsecore/sink.h | 4 ++ src/pulsecore/source-output.c | 91 +++++++++++++++++++++++++++++++ src/pulsecore/source-output.h | 4 ++ src/pulsecore/source.c | 101 +++++++++++++++++++++++++++++++++- src/pulsecore/source.h | 4 ++ 19 files changed, 602 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 4e6899c..d50fe47 100644 --- a/configure.ac +++ b/configure.ac @@ -1450,6 +1450,18 @@ AC_ARG_ENABLE(buffer-attr, AC_HELP_STRING([--enable-buffer-attr], [Use buffer at ],[TIZEN_BUFFER_ATTR=no]) AM_CONDITIONAL(TIZEN_BUFFER_ATTR, test "x$TIZEN_BUFFER_ATTR" = "xyes") +#### PCM DUMP #### + +AC_ARG_ENABLE(pcm-dump, AC_HELP_STRING([--enable-pcm-dump], [using pcm-dump]), + [ + case "${enableval}" in + yes) TIZEN_PCM_DUMP=yes ;; + no) TIZEN_PCM_DUMP=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-pcm-dump) ;; + esac +],[TIZEN_PCM_DUMP=no]) +AM_CONDITIONAL(TIZEN_PCM_DUMP, test "x$TIZEN_PCM_DUMP" = "xyes") + ################################### # Output # ################################### @@ -1700,6 +1712,7 @@ echo " Tizen dlog: ${TIZEN_DLOG} buffer attributes: ${TIZEN_BUFFER_ATTR} + pcm dump: ${TIZEN_PCM_DUMP} " if test "${ENABLE_SPEEX}" = "no" && test "${ENABLE_WEBRTC}" = "no" && test "${ENABLE_ADRIAN_EC}" = "no" ; then diff --git a/packaging/pulseaudio.spec b/packaging/pulseaudio.spec index 2c77796..d703f93 100644 --- a/packaging/pulseaudio.spec +++ b/packaging/pulseaudio.spec @@ -3,7 +3,7 @@ Name: pulseaudio Summary: Improved Linux sound server Version: 11.1 -Release: 5 +Release: 6 Group: Multimedia/Audio License: LGPL-2.1 URL: http://pulseaudio.org @@ -198,6 +198,7 @@ NOCONFIGURE=yes ./bootstrap.sh --with-access-group=pulse-access \ --enable-dlog \ --enable-buffer-attr \ + --enable-pcm-dump \ %__make %{?_smp_mflags} V=0 diff --git a/pulsecore.pc.in b/pulsecore.pc.in index 745a86f..bec612b 100644 --- a/pulsecore.pc.in +++ b/pulsecore.pc.in @@ -7,4 +7,4 @@ Name: pulsecore Description: Module building interface for pulseaudio Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lpulsecore-@PA_MAJORMINOR@ -lpulse -L${libdir}/pulse-@PA_MAJORMINOR@/modules -lprotocol-native -L${libdir}/pulseaudio -lpulsecommon-@PA_MAJORMINOR@ @PTHREAD_LIBS@ -Cflags: -D_REENTRANT -DTIZEN_BUFFER_ATTR -D__INCLUDED_FROM_PULSE_AUDIO -DPA_DEFAULT_CONFIG_DIR=\"@PA_DEFAULT_CONFIG_DIR@\" -I${includedir} +Cflags: -D_REENTRANT -DTIZEN_BUFFER_ATTR -DTIZEN_PCM_DUMP -D__INCLUDED_FROM_PULSE_AUDIO -DPA_DEFAULT_CONFIG_DIR=\"@PA_DEFAULT_CONFIG_DIR@\" -I${includedir} diff --git a/src/Makefile.am b/src/Makefile.am index 8134cbb..7f84597 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -217,6 +217,10 @@ pactl_SOURCES = utils/pactl.c pactl_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la $(LIBSNDFILE_LIBS) pactl_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS) pactl_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) +if TIZEN_PCM_DUMP +pactl_CFLAGS += $(VCONF_CFLAGS) -DTIZEN_PCM_DUMP +pactl_LDADD += $(VCONF_LIBS) +endif pasuspender_SOURCES = utils/pasuspender.c pasuspender_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la @@ -1045,6 +1049,10 @@ libpulse_la_CFLAGS += $(DBUS_CFLAGS) libpulse_la_LIBADD += $(DBUS_LIBS) endif +if TIZEN_PCM_DUMP +libpulse_la_CFLAGS += -DTIZEN_PCM_DUMP +endif + libpulse_simple_la_SOURCES = pulse/simple.c pulse/simple.h libpulse_simple_la_CFLAGS = $(AM_CFLAGS) libpulse_simple_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la @@ -1211,6 +1219,10 @@ libpulsecore_@PA_MAJORMINOR@_la_CFLAGS += $(LIBSAMPLERATE_CFLAGS) libpulsecore_@PA_MAJORMINOR@_la_LIBADD += $(LIBSAMPLERATE_LIBS) endif +if TIZEN_PCM_DUMP +libpulsecore_@PA_MAJORMINOR@_la_CFLAGS += -DTIZEN_PCM_DUMP +endif + # We split the foreign code off to not be annoyed by warnings we don't care about noinst_LTLIBRARIES += libpulsecore-foreign.la @@ -1282,6 +1294,9 @@ endif if TIZEN_BUFFER_ATTR libprotocol_native_la_CFLAGS += -DTIZEN_BUFFER_ATTR endif +if TIZEN_PCM_DUMP +libprotocol_native_la_CFLAGS += -DTIZEN_PCM_DUMP +endif if HAVE_ESOUND libprotocol_esound_la_SOURCES = pulsecore/protocol-esound.c pulsecore/protocol-esound.h pulsecore/esound.h diff --git a/src/map-file b/src/map-file index 93a62b8..38fe1ad 100644 --- a/src/map-file +++ b/src/map-file @@ -93,6 +93,8 @@ pa_context_set_default_sink; pa_context_set_default_source; pa_context_set_event_callback; pa_context_set_name; +pa_context_set_pcm_dump; +pa_context_set_pcm_dump_option; pa_context_set_sink_input_mute; pa_context_set_sink_input_volume; pa_context_set_sink_mute_by_index; diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index 510d784..d868655 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -2184,3 +2184,49 @@ pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, in return o; } + +#ifdef TIZEN_PCM_DUMP +pa_operation* pa_context_set_pcm_dump(pa_context *c, uint32_t dump_type, int dump_type_enable, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_PCM_DUMP, &tag); + pa_tagstruct_putu32(t, dump_type); + pa_tagstruct_put_boolean(t, dump_type_enable); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} + +pa_operation* pa_context_set_pcm_dump_option(pa_context *c, uint32_t dump_option, int dump_option_enable, pa_context_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(c); + pa_assert(PA_REFCNT_VALUE(c) >= 1); + + PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE); + + o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(c, PA_COMMAND_SET_PCM_DUMP_OPTION, &tag); + pa_tagstruct_putu32(t, dump_option); + pa_tagstruct_put_boolean(t, dump_option_enable); + pa_pstream_send_tagstruct(c->pstream, t); + pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return o; +} +#endif diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index 43389b7..4f4684e 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -550,6 +550,11 @@ pa_operation* pa_context_set_card_profile_by_name(pa_context *c, const char*name /** Set the latency offset of a port. \since 3.0 */ pa_operation* pa_context_set_port_latency_offset(pa_context *c, const char *card_name, const char *port_name, int64_t offset, pa_context_success_cb_t cb, void *userdata); +#ifdef TIZEN_PCM_DUMP +pa_operation* pa_context_set_pcm_dump(pa_context *c, uint32_t dump_type, int dump_type_enable, pa_context_success_cb_t cb, void *userdata); +pa_operation* pa_context_set_pcm_dump_option(pa_context *c, uint32_t dump_option, int dump_option_enable, pa_context_success_cb_t cb, void *userdata); +#endif + /** @} */ /** @{ \name Sink Inputs */ diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c index 454c566..ba48426 100644 --- a/src/pulsecore/core.c +++ b/src/pulsecore/core.c @@ -160,6 +160,10 @@ pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t pa_core_check_idle(c); c->state = PA_CORE_RUNNING; +#ifdef TIZEN_PCM_DUMP + c->pcm_dump = 0; + c->pcm_dump_option = 0; +#endif return c; } diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index 79a095d..125a560 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -141,6 +141,31 @@ typedef enum pa_core_hook { PA_CORE_HOOK_MAX } pa_core_hook_t; +#ifdef TIZEN_PCM_DUMP +#define PA_PCM_DUMP_PATH_PREFIX "/tmp/pcm" +#define PA_PCM_DUMP_VCONF_KEY "memory/private/sound/pcm_dump" + +enum { + PA_PCM_DUMP_GST_DECODER_OUT = 0x00000001U, + PA_PCM_DUMP_GST_RESAMPLER_IN = 0x00000008U, + PA_PCM_DUMP_GST_RESAMPLER_OUT = 0x00000010U, + PA_PCM_DUMP_GST_AUDIO_SINK_IN = 0x00000400U, + PA_PCM_DUMP_PA_STREAM_WRITE = 0x00000800U, + PA_PCM_DUMP_PA_SINK_INPUT = 0x00002000U, + PA_PCM_DUMP_PA_SINK = 0x00004000U, + PA_PCM_DUMP_PA_SOURCE = 0x00020000U, + PA_PCM_DUMP_PA_SOURCE_OUTPUT = 0x00040000U, + PA_PCM_DUMP_PA_STREAM_READ = 0x00100000U, + PA_PCM_DUMP_GST_AUDIO_SRC_OUT = 0x00200000U, + PA_PCM_DUMP_GST_ENCODER_IN = 0x80000000U, +}; + +enum { + PA_PCM_DUMP_OPTION_SEPARATED = 0x0001U, + PA_PCM_DUMP_OPTION_MONITOR = 0x0002U, +}; +#endif + /* The core structure of PulseAudio. Every PulseAudio daemon contains * exactly one of these. It is used for storing kind of global * variables for the daemon. */ @@ -227,6 +252,10 @@ struct pa_core { /* hooks */ pa_hook hooks[PA_CORE_HOOK_MAX]; +#ifdef TIZEN_PCM_DUMP + uint32_t pcm_dump; + uint32_t pcm_dump_option; +#endif }; PA_DECLARE_PUBLIC_CLASS(pa_core); diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h index 70338b9..faaa6f5 100644 --- a/src/pulsecore/native-common.h +++ b/src/pulsecore/native-common.h @@ -187,6 +187,11 @@ enum { * BOTH DIRECTIONS */ PA_COMMAND_REGISTER_MEMFD_SHMID, +#ifdef TIZEN_PCM_DUMP + PA_COMMAND_SET_PCM_DUMP, + PA_COMMAND_SET_PCM_DUMP_OPTION, +#endif + PA_COMMAND_MAX }; diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 3c30363..182758e 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -4935,6 +4935,57 @@ static void command_set_port_latency_offset(pa_pdispatch *pd, uint32_t command, pa_pstream_send_simple_ack(c->pstream, tag); } +#ifdef TIZEN_PCM_DUMP +static void command_set_pcm_dump(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); + uint32_t dump_type; + bool dump_type_enable; + + pa_native_connection_assert_ref(c); + pa_assert(t); + + if (pa_tagstruct_getu32(t, &dump_type) < 0 || + pa_tagstruct_get_boolean(t, &dump_type_enable) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); + + if (dump_type_enable) + c->protocol->core->pcm_dump |= dump_type; + else + c->protocol->core->pcm_dump &= ~dump_type; + + pa_pstream_send_simple_ack(c->pstream, tag); +} + +static void command_set_pcm_dump_option(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { + pa_native_connection *c = PA_NATIVE_CONNECTION(userdata); + uint32_t dump_option; + bool dump_option_enable; + + pa_native_connection_assert_ref(c); + pa_assert(t); + + if (pa_tagstruct_getu32(t, &dump_option) < 0 || + pa_tagstruct_get_boolean(t, &dump_option_enable) < 0 || + !pa_tagstruct_eof(t)) { + protocol_error(c); + return; + } + + CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); + + if (dump_option_enable) + c->protocol->core->pcm_dump_option |= dump_option; + else + c->protocol->core->pcm_dump_option &= ~dump_option; + + pa_pstream_send_simple_ack(c->pstream, tag); +} +#endif static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_ERROR] = NULL, [PA_COMMAND_TIMEOUT] = NULL, @@ -5041,7 +5092,11 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = { [PA_COMMAND_REGISTER_MEMFD_SHMID] = command_register_memfd_shmid, - [PA_COMMAND_EXTENSION] = command_extension + [PA_COMMAND_EXTENSION] = command_extension, +#ifdef TIZEN_PCM_DUMP + [PA_COMMAND_SET_PCM_DUMP] = command_set_pcm_dump, + [PA_COMMAND_SET_PCM_DUMP_OPTION] = command_set_pcm_dump_option, +#endif }; /*** pstream callbacks ***/ diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 4155b69..0169815 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -24,6 +24,9 @@ #include #include +#ifdef TIZEN_PCM_DUMP +#include +#endif #include #include @@ -53,6 +56,71 @@ struct volume_factor_entry { pa_cvolume volume; }; +#ifdef TIZEN_PCM_DUMP +static void pa_sink_input_write_pcm_dump(pa_sink_input *i, pa_memchunk *chunk) +{ + char *dump_time = NULL, *dump_path_surfix = NULL; + const char *s_device_api_str, *card_name_str, *device_idx_str; + struct timeval now; + struct tm tm; + char datetime[7]; + + /* open file for dump pcm */ + if (i->core->pcm_dump & PA_PCM_DUMP_PA_SINK_INPUT && !i->pcm_dump_fp && i->state == PA_SINK_INPUT_RUNNING) { + pa_gettimeofday(&now); + localtime_r(&now.tv_sec, &tm); + memset(&datetime[0], 0x00, sizeof(datetime)); + strftime(&datetime[0], sizeof(datetime), "%H%M%S", &tm); + dump_time = pa_sprintf_malloc("%s.%03ld", &datetime[0], now.tv_usec / 1000); + + if ((s_device_api_str = pa_proplist_gets(i->sink->proplist, PA_PROP_DEVICE_API))) { + if (pa_streq(s_device_api_str, "alsa")) { + card_name_str = pa_proplist_gets(i->sink->proplist, "alsa.card_name"); + device_idx_str = pa_proplist_gets(i->sink->proplist, "alsa.device"); + dump_path_surfix = pa_sprintf_malloc("%s.%s", pa_strnull(card_name_str), pa_strnull(device_idx_str)); + } else { + dump_path_surfix = pa_sprintf_malloc("%s", s_device_api_str); + } + } else { + dump_path_surfix = pa_sprintf_malloc("%s", i->sink->name); + } + + i->dump_path = pa_sprintf_malloc("%s_%s_pa-input%d-sink%d-%s_%dch_%d.raw", PA_PCM_DUMP_PATH_PREFIX, pa_strempty(dump_time), + i->index, i->sink->index, pa_strempty(dump_path_surfix), i->sample_spec.channels, i->sample_spec.rate); + if (i->dump_path) { + i->pcm_dump_fp = fopen(i->dump_path, "w"); + if (!i->pcm_dump_fp) + pa_log_warn("%s open failed", i->dump_path); + else + pa_log_info("%s opened", i->dump_path); + } + pa_xfree(dump_time); + pa_xfree(dump_path_surfix); + /* close file for dump pcm when config is changed */ + } else if (~i->core->pcm_dump & PA_PCM_DUMP_PA_SINK_INPUT && i->pcm_dump_fp) { + fclose(i->pcm_dump_fp); + pa_log_info("%s closed", i->dump_path); + pa_xfree(i->dump_path); + i->pcm_dump_fp = NULL; + } + + /* dump pcm */ + if (i->pcm_dump_fp) { + void *ptr = NULL; + + ptr = pa_memblock_acquire(chunk->memblock); + if (ptr) { + i->dump_length = chunk->length; + fwrite((uint8_t *)ptr + chunk->index, 1, chunk->length, i->pcm_dump_fp); + } else { + i->dump_length = 0; + pa_log_warn("pa_memblock_acquire is failed. ptr is NULL"); + } + pa_memblock_release(chunk->memblock); + } +} +#endif + static struct volume_factor_entry *volume_factor_entry_new(const char *key, const pa_cvolume *volume) { struct volume_factor_entry *entry; @@ -526,6 +594,11 @@ int pa_sink_input_new( reset_callbacks(i); i->userdata = NULL; +#ifdef TIZEN_PCM_DUMP + i->pcm_dump_fp = NULL; + i->dump_path = NULL; + i->dump_length = 0; +#endif i->thread_info.state = i->state; i->thread_info.attached = false; @@ -624,6 +697,15 @@ static void sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) for (ssync = i->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next) pa_sink_input_set_state_within_thread(ssync, state); } +#ifdef TIZEN_PCM_DUMP + if (i->state == PA_SINK_INPUT_RUNNING && i->pcm_dump_fp && (i->core->pcm_dump_option & PA_PCM_DUMP_OPTION_SEPARATED)) { + /* close file for dump pcm */ + fclose(i->pcm_dump_fp); + pa_log_info("%s closed", i->dump_path); + pa_xfree(i->dump_path); + i->pcm_dump_fp = NULL; + } +#endif update_n_corked(i, state); i->state = state; @@ -750,6 +832,15 @@ static void sink_input_free(pa_object *o) { if (i->thread_info.resampler) pa_resampler_free(i->thread_info.resampler); +#ifdef TIZEN_PCM_DUMP + /* close file for dump pcm */ + if (i->pcm_dump_fp) { + fclose(i->pcm_dump_fp); + pa_log_info("%s closed", i->dump_path); + pa_xfree(i->dump_path); + i->pcm_dump_fp = NULL; + } +#endif if (i->format) pa_format_info_free(i->format); @@ -1024,6 +1115,9 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink bytes */, pa pa_cvolume_mute(volume, i->sink->sample_spec.channels); else *volume = i->thread_info.soft_volume; +#ifdef TIZEN_PCM_DUMP + pa_sink_input_write_pcm_dump(i, chunk); +#endif } /* Called from thread context */ @@ -1038,6 +1132,18 @@ void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec * #ifdef SINK_INPUT_DEBUG pa_log_debug("dropping %lu", (unsigned long) nbytes); #endif +#ifdef TIZEN_PCM_DUMP + if (i->pcm_dump_fp && i->dump_length) { + int64_t seek_length; + + seek_length = (int64_t) (nbytes - i->dump_length); + + if (seek_length < 0) + fseeko(i->pcm_dump_fp, (off_t)seek_length, SEEK_CUR); + + i->dump_length = 0; + } +#endif pa_memblockq_drop(i->thread_info.render_memblockq, nbytes); } @@ -1081,6 +1187,11 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam if (i->thread_info.rewrite_nbytes == (size_t) -1) { +#ifdef TIZEN_PCM_DUMP + /* rewind pcm */ + if (i->pcm_dump_fp) + fseeko(i->pcm_dump_fp, (off_t)pa_memblockq_get_length(i->thread_info.render_memblockq) * (-1), SEEK_CUR); +#endif /* We were asked to drop all buffered data, and rerequest new * data from implementor the next time peek() is called */ @@ -1102,6 +1213,11 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam if (amount > 0) { pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) amount); +#ifdef TIZEN_PCM_DUMP + /* rewind pcm */ + if (i->pcm_dump_fp) + fseeko(i->pcm_dump_fp, (off_t)amount * (-1), SEEK_CUR); +#endif /* Tell the implementor */ if (i->process_rewind) i->process_rewind(i, amount); diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index f957a0e..88ae1dd 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -250,6 +250,11 @@ struct pa_sink_input { pa_hashmap *direct_outputs; } thread_info; +#ifdef TIZEN_PCM_DUMP + FILE *pcm_dump_fp; + char *dump_path; + uint32_t dump_length; +#endif void *userdata; }; diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index cbca889..04c688c 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -25,6 +25,9 @@ #include #include #include +#ifdef TIZEN_PCM_DUMP +#include +#endif #include #include @@ -75,6 +78,69 @@ static void pa_sink_volume_change_push(pa_sink *s); static void pa_sink_volume_change_flush(pa_sink *s); static void pa_sink_volume_change_rewind(pa_sink *s, size_t nbytes); +#ifdef TIZEN_PCM_DUMP +static void pa_sink_write_pcm_dump(pa_sink *s, pa_memchunk *chunk) +{ + char *dump_time = NULL, *dump_path_surfix = NULL; + const char *s_device_api_str, *card_name_str, *device_idx_str; + struct timeval now; + struct tm tm; + char datetime[7]; + + /* open file for dump pcm */ + if (s->core->pcm_dump & PA_PCM_DUMP_PA_SINK && !s->pcm_dump_fp && s->state == PA_SINK_RUNNING) { + pa_gettimeofday(&now); + localtime_r(&now.tv_sec, &tm); + memset(&datetime[0], 0x00, sizeof(datetime)); + strftime(&datetime[0], sizeof(datetime), "%H%M%S", &tm); + dump_time = pa_sprintf_malloc("%s.%03ld", &datetime[0], now.tv_usec / 1000); + + if ((s_device_api_str = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_API))) { + if (pa_streq(s_device_api_str, "alsa")) { + card_name_str = pa_proplist_gets(s->proplist, "alsa.card_name"); + device_idx_str = pa_proplist_gets(s->proplist, "alsa.device"); + dump_path_surfix = pa_sprintf_malloc("%s.%s", pa_strnull(card_name_str), pa_strnull(device_idx_str)); + } else { + dump_path_surfix = pa_sprintf_malloc("%s", s_device_api_str); + } + } else { + dump_path_surfix = pa_sprintf_malloc("%s", s->name); + } + + s->dump_path = pa_sprintf_malloc("%s_%s_pa-sink%d-%s_%dch_%d.raw", PA_PCM_DUMP_PATH_PREFIX, pa_strempty(dump_time), + s->index, pa_strempty(dump_path_surfix), s->sample_spec.channels, s->sample_spec.rate); + if (s->dump_path) { + s->pcm_dump_fp = fopen(s->dump_path, "w"); + if (!s->pcm_dump_fp) + pa_log_warn("%s open failed", s->dump_path); + else + pa_log_info("%s opened", s->dump_path); + } + pa_xfree(dump_time); + pa_xfree(dump_path_surfix); + /* close file for dump pcm when config is changed */ + } else if (~s->core->pcm_dump & PA_PCM_DUMP_PA_SINK && s->pcm_dump_fp) { + fclose(s->pcm_dump_fp); + pa_log_info("%s closed", s->dump_path); + pa_xfree(s->dump_path); + s->pcm_dump_fp = NULL; + } + + /* dump pcm */ + if (s->pcm_dump_fp) { + void *ptr = NULL; + + ptr = pa_memblock_acquire(chunk->memblock); + if (ptr) + fwrite((uint8_t *)ptr + chunk->index, 1, chunk->length, s->pcm_dump_fp); + else + pa_log_warn("pa_memblock_acquire is failed. ptr is NULL"); + + pa_memblock_release(chunk->memblock); + } +} +#endif + pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data) { pa_assert(data); @@ -316,6 +382,10 @@ pa_sink* pa_sink_new( s->save_volume = data->save_volume; s->save_muted = data->save_muted; +#ifdef TIZEN_PCM_DUMP + s->pcm_dump_fp = NULL; + s->dump_path = NULL; +#endif pa_silence_memchunk_get( &core->silence_cache, @@ -428,6 +498,16 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) { } s->state = state; +#ifdef TIZEN_PCM_DUMP + /* close file for dump pcm */ + if (s->pcm_dump_fp && (s->core->pcm_dump_option & PA_PCM_DUMP_OPTION_SEPARATED) && + state == PA_SINK_IDLE && original_state == PA_SINK_RUNNING) { + fclose(s->pcm_dump_fp); + pa_log_info("%s closed", s->dump_path); + pa_xfree(s->dump_path); + s->pcm_dump_fp = NULL; + } +#endif if (state != PA_SINK_UNLINKED) { /* if we enter UNLINKED state pa_sink_unlink() will fire the appropriate events */ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], s); @@ -754,6 +834,15 @@ static void sink_free(pa_object *o) { if (s->ports) pa_hashmap_free(s->ports); +#ifdef TIZEN_PCM_DUMP + /* close file for dump pcm */ + if (s->pcm_dump_fp) { + fclose(s->pcm_dump_fp); + pa_log_info("%s closed", s->dump_path); + pa_xfree(s->dump_path); + s->pcm_dump_fp = NULL; + } +#endif pa_xfree(s); } @@ -1017,6 +1106,11 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) { pa_log_debug("Processing rewind..."); if (s->flags & PA_SINK_DEFERRED_VOLUME) pa_sink_volume_change_rewind(s, nbytes); +#ifdef TIZEN_PCM_DUMP + /* rewind pcm */ + if (s->pcm_dump_fp) + fseeko(s->pcm_dump_fp, (off_t)nbytes * (-1), SEEK_CUR); +#endif } PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) { @@ -1250,6 +1344,9 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) { inputs_drop(s, info, n, result); +#ifdef TIZEN_PCM_DUMP + pa_sink_write_pcm_dump(s, result); +#endif pa_sink_unref(s); } @@ -1335,6 +1432,9 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) { inputs_drop(s, info, n, target); +#ifdef TIZEN_PCM_DUMP + pa_sink_write_pcm_dump(s, target); +#endif pa_sink_unref(s); } diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 0e79cf3..2a0eeca 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -306,6 +306,10 @@ struct pa_sink { int32_t volume_change_extra_delay; } thread_info; +#ifdef TIZEN_PCM_DUMP + FILE *pcm_dump_fp; + char *dump_path; +#endif void *userdata; }; diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index a4c99af..4b1c4a2 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -24,6 +24,9 @@ #include #include #include +#ifdef TIZEN_PCM_DUMP +#include +#endif #include #include @@ -46,6 +49,68 @@ PA_DEFINE_PUBLIC_CLASS(pa_source_output, pa_msgobject); static void source_output_free(pa_object* mo); static void set_real_ratio(pa_source_output *o, const pa_cvolume *v); +#ifdef TIZEN_PCM_DUMP +static void pa_source_output_write_pcm_dump(pa_source_output *o, pa_memchunk *chunk) +{ + char *dump_time = NULL, *dump_path_surfix = NULL; + const char *s_device_api_str, *card_name_str, *device_idx_str; + struct timeval now; + struct tm tm; + char datetime[7]; + + /* open file for dump pcm */ + if (o->core->pcm_dump & PA_PCM_DUMP_PA_SOURCE_OUTPUT && !o->pcm_dump_fp && o->state == PA_SOURCE_OUTPUT_RUNNING) { + pa_gettimeofday(&now); + localtime_r(&now.tv_sec, &tm); + memset(&datetime[0], 0x00, sizeof(datetime)); + strftime(&datetime[0], sizeof(datetime), "%H%M%S", &tm); + dump_time = pa_sprintf_malloc("%s.%03ld", &datetime[0], now.tv_usec / 1000); + + if ((s_device_api_str = pa_proplist_gets(o->source->proplist, PA_PROP_DEVICE_API))) { + if (pa_streq(s_device_api_str, "alsa")) { + card_name_str = pa_proplist_gets(o->source->proplist, "alsa.card_name"); + device_idx_str = pa_proplist_gets(o->source->proplist, "alsa.device"); + dump_path_surfix = pa_sprintf_malloc("%s.%s", pa_strnull(card_name_str), pa_strnull(device_idx_str)); + } else { + dump_path_surfix = pa_sprintf_malloc("%s", s_device_api_str); + } + } else { + dump_path_surfix = pa_sprintf_malloc("%s", o->source->name); + } + + o->dump_path = pa_sprintf_malloc("%s_%s_pa-output%d-source%d-%s_%dch_%d.raw", PA_PCM_DUMP_PATH_PREFIX, pa_strempty(dump_time), + o->index, o->source->index, pa_strempty(dump_path_surfix), o->sample_spec.channels, o->sample_spec.rate); + if (o->dump_path) { + o->pcm_dump_fp = fopen(o->dump_path, "w"); + if (!o->pcm_dump_fp) + pa_log_warn("%s open failed", o->dump_path); + else + pa_log_info("%s opened", o->dump_path); + } + pa_xfree(dump_time); + pa_xfree(dump_path_surfix); + /* close file for dump pcm when config is changed */ + } else if (~o->core->pcm_dump & PA_PCM_DUMP_PA_SOURCE_OUTPUT && o->pcm_dump_fp) { + fclose(o->pcm_dump_fp); + pa_log_info("%s closed", o->dump_path); + pa_xfree(o->dump_path); + o->pcm_dump_fp = NULL; + } + + /* dump pcm */ + if (o->pcm_dump_fp) { + void *ptr = NULL; + + ptr = pa_memblock_acquire(chunk->memblock); + if (ptr) + fwrite((uint8_t *)ptr + chunk->index, 1, chunk->length, o->pcm_dump_fp); + else + pa_log_warn("pa_memblock_acquire is failed. ptr is NULL"); + + pa_memblock_release(chunk->memblock); + } +} +#endif pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data) { pa_assert(data); @@ -537,6 +602,15 @@ static void source_output_set_state(pa_source_output *o, pa_source_output_state_ /* If the source is not valid, pa_source_output_set_state_within_thread() must be called directly */ pa_source_output_set_state_within_thread(o, state); +#ifdef TIZEN_PCM_DUMP + if (o->state == PA_SOURCE_OUTPUT_RUNNING && o->pcm_dump_fp && (o->core->pcm_dump_option & PA_PCM_DUMP_OPTION_SEPARATED)) { + /* close file for dump pcm */ + fclose(o->pcm_dump_fp); + pa_log_info("%s closed", o->dump_path); + pa_xfree(o->dump_path); + o->pcm_dump_fp = NULL; + } +#endif update_n_corked(o, state); o->state = state; @@ -631,6 +705,15 @@ static void source_output_free(pa_object* mo) { if (o->thread_info.resampler) pa_resampler_free(o->thread_info.resampler); +#ifdef TIZEN_PCM_DUMP + /* close file for dump pcm */ + if (o->pcm_dump_fp) { + fclose(o->pcm_dump_fp); + pa_log_info("%s closed", o->dump_path); + pa_xfree(o->dump_path); + o->pcm_dump_fp = NULL; + } +#endif if (o->format) pa_format_info_free(o->format); @@ -825,6 +908,9 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { pa_memblock_unref(qchunk.memblock); pa_memblockq_drop(o->thread_info.delay_memblockq, qchunk.length); } +#ifdef TIZEN_PCM_DUMP + pa_source_output_write_pcm_dump(o, (pa_memchunk *)chunk); +#endif } /* Called from thread context */ @@ -845,6 +931,11 @@ void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in so nbytes = pa_resampler_result(o->thread_info.resampler, nbytes); pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) nbytes); +#ifdef TIZEN_PCM_DUMP + /* rewind pcm */ + if (o->pcm_dump_fp) + fseeko(o->pcm_dump_fp, (off_t)nbytes * (-1), SEEK_CUR); +#endif if (nbytes > 0) o->process_rewind(o, nbytes); diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index 40e4e3a..2388fbe 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -208,6 +208,10 @@ struct pa_source_output { pa_sink_input *direct_on_input; /* may be NULL */ } thread_info; +#ifdef TIZEN_PCM_DUMP + FILE *pcm_dump_fp; + char *dump_path; +#endif void *userdata; }; diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index e0d32af..878fc50 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -24,6 +24,9 @@ #include #include +#ifdef TIZEN_PCM_DUMP +#include +#endif #include #include @@ -65,6 +68,69 @@ static void source_free(pa_object *o); static void pa_source_volume_change_push(pa_source *s); static void pa_source_volume_change_flush(pa_source *s); +#ifdef TIZEN_PCM_DUMP +static void pa_source_write_pcm_dump(pa_source *s, pa_memchunk *chunk) +{ + char *dump_time = NULL, *dump_path_surfix = NULL; + const char *s_device_api_str, *card_name_str, *device_idx_str; + struct timeval now; + struct tm tm; + char datetime[7]; + + /* open file for dump pcm */ + if (s->core->pcm_dump & PA_PCM_DUMP_PA_SOURCE && !s->pcm_dump_fp && s->state == PA_SOURCE_RUNNING && + !(~s->core->pcm_dump_option & PA_PCM_DUMP_OPTION_MONITOR && s->monitor_of)) { + pa_gettimeofday(&now); + localtime_r(&now.tv_sec, &tm); + memset(&datetime[0], 0x00, sizeof(datetime)); + strftime(&datetime[0], sizeof(datetime), "%H%M%S", &tm); + dump_time = pa_sprintf_malloc("%s.%03ld", &datetime[0], now.tv_usec / 1000); + + if ((s_device_api_str = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_API))) { + if (pa_streq(s_device_api_str, "alsa")) { + card_name_str = pa_proplist_gets(s->proplist, "alsa.card_name"); + device_idx_str = pa_proplist_gets(s->proplist, "alsa.device"); + dump_path_surfix = pa_sprintf_malloc("%s.%s%s", pa_strnull(card_name_str), pa_strnull(device_idx_str), s->monitor_of ? ".monitor" : ""); + } else { + dump_path_surfix = pa_sprintf_malloc("%s%s", s_device_api_str, s->monitor_of ? ".monitor" : ""); + } + } else { + dump_path_surfix = pa_sprintf_malloc("%s", s->name); + } + + s->dump_path = pa_sprintf_malloc("%s_%s_pa-src%d-%s_%dch_%d.raw", PA_PCM_DUMP_PATH_PREFIX, pa_strempty(dump_time), + s->index, pa_strempty(dump_path_surfix), s->sample_spec.channels, s->sample_spec.rate); + if (s->dump_path) { + s->pcm_dump_fp = fopen(s->dump_path, "w"); + if (!s->pcm_dump_fp) + pa_log_warn("%s open failed", s->dump_path); + else + pa_log_info("%s opened", s->dump_path); + } + pa_xfree(dump_time); + pa_xfree(dump_path_surfix); + /* close file for dump pcm when config is changed */ + } else if (~s->core->pcm_dump & PA_PCM_DUMP_PA_SOURCE && s->pcm_dump_fp) { + fclose(s->pcm_dump_fp); + pa_log_info("%s closed", s->dump_path); + pa_xfree(s->dump_path); + s->pcm_dump_fp = NULL; + } + + /* dump pcm */ + if (s->pcm_dump_fp) { + void *ptr = NULL; + + ptr = pa_memblock_acquire(chunk->memblock); + if (ptr) + fwrite((uint8_t *)ptr + chunk->index, 1, chunk->length, s->pcm_dump_fp); + else + pa_log_warn("pa_memblock_acquire is failed. ptr is NULL"); + + pa_memblock_release(chunk->memblock); + } +} +#endif pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data) { pa_assert(data); @@ -304,6 +370,10 @@ pa_source* pa_source_new( s->save_volume = data->save_volume; s->save_muted = data->save_muted; +#ifdef TIZEN_PCM_DUMP + s->pcm_dump_fp = NULL; + s->dump_path = NULL; +#endif pa_silence_memchunk_get( &core->silence_cache, @@ -382,6 +452,16 @@ static int source_set_state(pa_source *s, pa_source_state_t state) { } s->state = state; +#ifdef TIZEN_PCM_DUMP + /* close file for dump pcm */ + if (s->pcm_dump_fp && (s->core->pcm_dump_option & PA_PCM_DUMP_OPTION_SEPARATED) && + state == PA_SOURCE_IDLE && original_state == PA_SOURCE_RUNNING) { + fclose(s->pcm_dump_fp); + pa_log_info("%s closed", s->dump_path); + pa_xfree(s->dump_path); + s->pcm_dump_fp = NULL; + } +#endif if (state != PA_SOURCE_UNLINKED) { /* if we enter UNLINKED state pa_source_unlink() will fire the appropriate events */ pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], s); @@ -687,7 +767,15 @@ static void source_free(pa_object *o) { if (s->ports) pa_hashmap_free(s->ports); - +#ifdef TIZEN_PCM_DUMP + /* close file for dump pcm */ + if (s->pcm_dump_fp) { + fclose(s->pcm_dump_fp); + pa_log_info("%s closed", s->dump_path); + pa_xfree(s->dump_path); + s->pcm_dump_fp = NULL; + } +#endif pa_xfree(s); } @@ -900,6 +988,11 @@ void pa_source_process_rewind(pa_source *s, size_t nbytes) { return; pa_log_debug("Processing rewind..."); +#ifdef TIZEN_PCM_DUMP + /* rewind pcm */ + if (s->pcm_dump_fp) + fseeko(s->pcm_dump_fp, (off_t)nbytes * (-1), SEEK_CUR); +#endif PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state) { pa_source_output_assert_ref(o); @@ -919,6 +1012,9 @@ void pa_source_post(pa_source*s, const pa_memchunk *chunk) { if (s->thread_info.state == PA_SOURCE_SUSPENDED) return; +#ifdef TIZEN_PCM_DUMP + pa_source_write_pcm_dump(s, (pa_memchunk *)chunk); +#endif if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) { pa_memchunk vchunk = *chunk; @@ -961,6 +1057,9 @@ void pa_source_post_direct(pa_source*s, pa_source_output *o, const pa_memchunk * if (s->thread_info.state == PA_SOURCE_SUSPENDED) return; +#ifdef TIZEN_PCM_DUMP + pa_source_write_pcm_dump(s, (pa_memchunk *)chunk); +#endif if (s->thread_info.soft_muted || !pa_cvolume_is_norm(&s->thread_info.soft_volume)) { pa_memchunk vchunk = *chunk; diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index 1e33cde..6ca274a 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -248,6 +248,10 @@ struct pa_source { int32_t volume_change_extra_delay; } thread_info; +#ifdef TIZEN_PCM_DUMP + FILE *pcm_dump_fp; + char *dump_path; +#endif void *userdata; }; -- 2.7.4