Add support for pcm dump 37/169037/5
authorSangchul Lee <sc11.lee@samsung.com>
Fri, 2 Feb 2018 03:56:27 +0000 (12:56 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Fri, 9 Feb 2018 06:43:05 +0000 (06:43 +0000)
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 <sc11.lee@samsung.com>
19 files changed:
configure.ac
packaging/pulseaudio.spec
pulsecore.pc.in
src/Makefile.am
src/map-file
src/pulse/introspect.c
src/pulse/introspect.h
src/pulsecore/core.c
src/pulsecore/core.h
src/pulsecore/native-common.h
src/pulsecore/protocol-native.c
src/pulsecore/sink-input.c
src/pulsecore/sink-input.h
src/pulsecore/sink.c
src/pulsecore/sink.h
src/pulsecore/source-output.c
src/pulsecore/source-output.h
src/pulsecore/source.c
src/pulsecore/source.h

index 4e6899c..d50fe47 100644 (file)
@@ -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
index 2c77796..d703f93 100644 (file)
@@ -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
 
index 745a86f..bec612b 100644 (file)
@@ -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}
index 8134cbb..7f84597 100644 (file)
@@ -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
index 93a62b8..38fe1ad 100644 (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;
index 510d784..d868655 100644 (file)
@@ -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
index 43389b7..4f4684e 100644 (file)
@@ -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 */
index 454c566..ba48426 100644 (file)
@@ -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;
 }
index 79a095d..125a560 100644 (file)
@@ -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);
index 70338b9..faaa6f5 100644 (file)
@@ -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
 };
 
index 3c30363..182758e 100644 (file)
@@ -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 ***/
index 4155b69..0169815 100644 (file)
@@ -24,6 +24,9 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#ifdef TIZEN_PCM_DUMP
+#include <time.h>
+#endif
 
 #include <pulse/utf8.h>
 #include <pulse/xmalloc.h>
@@ -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);
index f957a0e..88ae1dd 100644 (file)
@@ -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;
 };
index cbca889..04c688c 100644 (file)
@@ -25,6 +25,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#ifdef TIZEN_PCM_DUMP
+#include <time.h>
+#endif
 
 #include <pulse/introspect.h>
 #include <pulse/format.h>
@@ -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);
 }
 
index 0e79cf3..2a0eeca 100644 (file)
@@ -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;
 };
 
index a4c99af..4b1c4a2 100644 (file)
@@ -24,6 +24,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#ifdef TIZEN_PCM_DUMP
+#include <time.h>
+#endif
 
 #include <pulse/utf8.h>
 #include <pulse/xmalloc.h>
@@ -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);
index 40e4e3a..2388fbe 100644 (file)
@@ -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;
 };
index e0d32af..878fc50 100644 (file)
@@ -24,6 +24,9 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#ifdef TIZEN_PCM_DUMP
+#include <time.h>
+#endif
 
 #include <pulse/format.h>
 #include <pulse/utf8.h>
@@ -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;
index 1e33cde..6ca274a 100644 (file)
@@ -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;
 };