From fd786bad6321dd800265b1e5ae8c7e1d2e272a94 Mon Sep 17 00:00:00 2001 From: Lukasz Marek Date: Mon, 24 Mar 2014 17:49:04 +0100 Subject: [PATCH 1/6] tools/uncoded_frame: fix audio codec generation Codec id is guessed from uninitialized sample format. Signed-off-by: Lukasz Marek --- tools/uncoded_frame.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/uncoded_frame.c b/tools/uncoded_frame.c index eafbbfde17..53e71eacbf 100644 --- a/tools/uncoded_frame.c +++ b/tools/uncoded_frame.c @@ -177,12 +177,12 @@ int main(int argc, char **argv) st->stream->codec->pix_fmt = st->link->format; break; case AVMEDIA_TYPE_AUDIO: - st->stream->codec->codec_id = - av_get_pcm_codec(st->stream->codec->sample_fmt, -1); st->stream->codec->channel_layout = st->link->channel_layout; st->stream->codec->channels = avfilter_link_get_channels(st->link); st->stream->codec->sample_rate = st->link->sample_rate; st->stream->codec->sample_fmt = st->link->format; + st->stream->codec->codec_id = + av_get_pcm_codec(st->stream->codec->sample_fmt, -1); break; default: av_assert0(!"reached"); From 27256e69ab2df625a431dfe67c6a7aa364992a48 Mon Sep 17 00:00:00 2001 From: Lukasz Marek Date: Mon, 24 Mar 2014 17:55:07 +0100 Subject: [PATCH 2/6] lavd/pulse_audio_enc: implement write_uncoded_frame callback Provided implementation doesn't support planar formats yet. Signed-off-by: Lukasz Marek --- libavdevice/pulse_audio_enc.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/libavdevice/pulse_audio_enc.c b/libavdevice/pulse_audio_enc.c index 4fb64ed471..03f713cb15 100644 --- a/libavdevice/pulse_audio_enc.c +++ b/libavdevice/pulse_audio_enc.c @@ -141,6 +141,24 @@ static int pulse_write_packet(AVFormatContext *h, AVPacket *pkt) return 0; } +static int pulse_write_frame(AVFormatContext *h, int stream_index, + AVFrame **frame, unsigned flags) +{ + AVPacket pkt; + + /* Planar formats are not supported yet. */ + if (flags & AV_WRITE_UNCODED_FRAME_QUERY) + return av_sample_fmt_is_planar(h->streams[stream_index]->codec->sample_fmt) ? + AVERROR(EINVAL) : 0; + + pkt.data = (*frame)->data[0]; + pkt.size = (*frame)->nb_samples * av_get_bytes_per_sample((*frame)->format) * (*frame)->channels; + pkt.dts = (*frame)->pkt_dts; + pkt.duration = av_frame_get_pkt_duration(*frame); + return pulse_write_packet(h, &pkt); +} + + static void pulse_get_output_timestamp(AVFormatContext *h, int stream, int64_t *dts, int64_t *wall) { PulseData *s = h->priv_data; @@ -178,6 +196,7 @@ AVOutputFormat ff_pulse_muxer = { .video_codec = AV_CODEC_ID_NONE, .write_header = pulse_write_header, .write_packet = pulse_write_packet, + .write_uncoded_frame = pulse_write_frame, .write_trailer = pulse_write_trailer, .get_output_timestamp = pulse_get_output_timestamp, .flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH, From cd50a44beb01582093b8115287cb51a7feb83f77 Mon Sep 17 00:00:00 2001 From: Lukasz Marek Date: Tue, 25 Feb 2014 01:06:06 +0100 Subject: [PATCH 3/6] lavu/mem: add av_dynarray_add_nofree function av_dynarray_add_nofree function have similar functionality as existing av_dynarray_add, but it doesn't deallocate memory on fails. Signed-off-by: Lukasz Marek --- doc/APIchanges | 3 +++ libavutil/mem.c | 13 +++++++++++++ libavutil/mem.h | 19 +++++++++++++++++-- libavutil/version.h | 2 +- 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/doc/APIchanges b/doc/APIchanges index 3890d1d78e..b8d8b8599d 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -15,6 +15,9 @@ libavutil: 2012-10-22 API changes, most recent first: +2014-03-xx - xxxxxxx - lavu 52.70.100 - mem.h + Add av_dynarray_add_nofree() function. + 2014-02-xx - xxxxxxx - lavu 53.08.0 - frame.h Add av_frame_remove_side_data() for removing a single side data instance from a frame. diff --git a/libavutil/mem.c b/libavutil/mem.c index e0d0d9040b..8226168eed 100644 --- a/libavutil/mem.c +++ b/libavutil/mem.c @@ -278,6 +278,19 @@ void *av_memdup(const void *p, size_t size) return ptr; } +int av_dynarray_add_nofree(void *tab_ptr, int *nb_ptr, void *elem) +{ + void **tab = *(void ***)tab_ptr; + + AV_DYNARRAY_ADD(INT_MAX, sizeof(*tab), tab, *nb_ptr, { + tab[*nb_ptr] = elem; + *(void ***)tab_ptr = tab; + }, { + return AVERROR(ENOMEM); + }); + return 0; +} + void av_dynarray_add(void *tab_ptr, int *nb_ptr, void *elem) { void **tab = *(void ***)tab_ptr; diff --git a/libavutil/mem.h b/libavutil/mem.h index 703ce81936..801c53ff51 100644 --- a/libavutil/mem.h +++ b/libavutil/mem.h @@ -276,10 +276,25 @@ void av_freep(void *ptr); * @param tab_ptr pointer to the array to grow * @param nb_ptr pointer to the number of elements in the array * @param elem element to add - * @see av_dynarray2_add() + * @see av_dynarray_add_nofree(), av_dynarray2_add() */ void av_dynarray_add(void *tab_ptr, int *nb_ptr, void *elem); +/** + * Add an element to a dynamic array. + * + * Function has the same functionality as av_dynarray_add(), + * but it doesn't free memory on fails. It returns error code + * instead and leave current buffer untouched. + * + * @param tab_ptr pointer to the array to grow + * @param nb_ptr pointer to the number of elements in the array + * @param elem element to add + * @return >=0 on success, negative otherwise. + * @see av_dynarray_add(), av_dynarray2_add() + */ +int av_dynarray_add_nofree(void *tab_ptr, int *nb_ptr, void *elem); + /** * Add an element of size elem_size to a dynamic array. * @@ -299,7 +314,7 @@ void av_dynarray_add(void *tab_ptr, int *nb_ptr, void *elem); * the new added element is not filled. * @return pointer to the data of the element to copy in the new allocated space. * If NULL, the new allocated space is left uninitialized." - * @see av_dynarray_add() + * @see av_dynarray_add(), av_dynarray_add_nofree() */ void *av_dynarray2_add(void **tab_ptr, int *nb_ptr, size_t elem_size, const uint8_t *elem_data); diff --git a/libavutil/version.h b/libavutil/version.h index 45f5adc804..644f157242 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -56,7 +56,7 @@ */ #define LIBAVUTIL_VERSION_MAJOR 52 -#define LIBAVUTIL_VERSION_MINOR 69 +#define LIBAVUTIL_VERSION_MINOR 70 #define LIBAVUTIL_VERSION_MICRO 100 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ From 85ed32d2ed15888052a4f55d878f7cfb192d0e0d Mon Sep 17 00:00:00 2001 From: Lukasz Marek Date: Tue, 25 Feb 2014 01:03:56 +0100 Subject: [PATCH 4/6] lavd/pulse_audio_common: add device detecting code Signed-off-by: Lukasz Marek --- libavdevice/pulse_audio_common.c | 177 ++++++++++++++++++++++++++++++- libavdevice/pulse_audio_common.h | 5 +- 2 files changed, 180 insertions(+), 2 deletions(-) diff --git a/libavdevice/pulse_audio_common.c b/libavdevice/pulse_audio_common.c index f7227f6549..cfe97bc7cb 100644 --- a/libavdevice/pulse_audio_common.c +++ b/libavdevice/pulse_audio_common.c @@ -1,5 +1,6 @@ /* - * Pulseaudio input + * Pulseaudio common + * Copyright (c) 2014 Lukasz Marek * Copyright (c) 2011 Luca Barbato * * This file is part of FFmpeg. @@ -21,6 +22,8 @@ #include "pulse_audio_common.h" #include "libavutil/attributes.h" +#include "libavutil/avstring.h" +#include "libavutil/mem.h" pa_sample_format_t av_cold ff_codec_id_to_pulse_format(enum AVCodecID codec_id) { @@ -39,3 +42,175 @@ pa_sample_format_t av_cold ff_codec_id_to_pulse_format(enum AVCodecID codec_id) default: return PA_SAMPLE_INVALID; } } + +enum PulseAudioLoopState { + PA_LOOP_INITIALIZING, + PA_LOOP_READY, + PA_LOOP_FINISHED +}; + +typedef struct PulseAudioDeviceList { + AVDeviceInfoList *devices; + int error_code; + int output; + char *default_device; +} PulseAudioDeviceList; + +static void pa_state_cb(pa_context *c, void *userdata) +{ + enum PulseAudioLoopState *loop_status = userdata; + + switch (pa_context_get_state(c)) { + case PA_CONTEXT_FAILED: + case PA_CONTEXT_TERMINATED: + *loop_status = PA_LOOP_FINISHED; + break; + case PA_CONTEXT_READY: + *loop_status = PA_LOOP_READY; + break; + default: + break; + } +} + +static void pulse_add_detected_device(PulseAudioDeviceList *info, + const char *name, const char *description) +{ + int ret; + AVDeviceInfo *new_device = NULL; + + if (info->error_code) + return; + + new_device = av_mallocz(sizeof(AVDeviceInfo)); + if (!new_device) { + info->error_code = AVERROR(ENOMEM); + return; + } + + new_device->device_description = av_strdup(description); + new_device->device_name = av_strdup(name); + + if (!new_device->device_description || !new_device->device_name) { + info->error_code = AVERROR(ENOMEM); + goto fail; + } + + if ((ret = av_dynarray_add_nofree(&info->devices->devices, + &info->devices->nb_devices, new_device)) < 0) { + info->error_code = ret; + goto fail; + } + return; + + fail: + av_free(new_device->device_description); + av_free(new_device->device_name); + av_free(new_device); + +} + +static void pulse_audio_source_device_cb(pa_context *c, const pa_source_info *dev, + int eol, void *userdata) +{ + if (!eol) + pulse_add_detected_device(userdata, dev->name, dev->description); +} + +static void pulse_audio_sink_device_cb(pa_context *c, const pa_sink_info *dev, + int eol, void *userdata) +{ + if (!eol) + pulse_add_detected_device(userdata, dev->name, dev->description); +} + +static void pulse_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) +{ + PulseAudioDeviceList *info = userdata; + if (info->output) + info->default_device = av_strdup(i->default_sink_name); + else + info->default_device = av_strdup(i->default_source_name); + if (!info->default_device) + info->error_code = AVERROR(ENOMEM); +} + +int ff_pulse_audio_get_devices(AVDeviceInfoList *devices, const char *server, int output) +{ + pa_mainloop *pa_ml = NULL; + pa_mainloop_api *pa_mlapi = NULL; + pa_operation *pa_op = NULL; + pa_context *pa_ctx = NULL; + enum pa_operation_state op_state; + enum PulseAudioLoopState loop_state = PA_LOOP_INITIALIZING; + PulseAudioDeviceList dev_list = { 0 }; + int i; + + dev_list.output = output; + dev_list.devices = devices; + devices->nb_devices = 0; + devices->devices = NULL; + if (!devices) + return AVERROR(EINVAL); + if (!(pa_ml = pa_mainloop_new())) + return AVERROR(ENOMEM); + if (!(pa_mlapi = pa_mainloop_get_api(pa_ml))) { + dev_list.error_code = AVERROR_EXTERNAL; + goto fail; + } + if (!(pa_ctx = pa_context_new(pa_mlapi, "Query devices"))) { + dev_list.error_code = AVERROR(ENOMEM); + goto fail; + } + pa_context_set_state_callback(pa_ctx, pa_state_cb, &loop_state); + if (pa_context_connect(pa_ctx, server, 0, NULL) < 0) { + dev_list.error_code = AVERROR_EXTERNAL; + goto fail; + } + + while (loop_state == PA_LOOP_INITIALIZING) + pa_mainloop_iterate(pa_ml, 1, NULL); + if (loop_state == PA_LOOP_FINISHED) { + dev_list.error_code = AVERROR_EXTERNAL; + goto fail; + } + + if (output) + pa_op = pa_context_get_sink_info_list(pa_ctx, pulse_audio_sink_device_cb, &dev_list); + else + pa_op = pa_context_get_source_info_list(pa_ctx, pulse_audio_source_device_cb, &dev_list); + while ((op_state = pa_operation_get_state(pa_op)) == PA_OPERATION_RUNNING) + pa_mainloop_iterate(pa_ml, 1, NULL); + if (op_state != PA_OPERATION_DONE) + dev_list.error_code = AVERROR_EXTERNAL; + pa_operation_unref(pa_op); + if (dev_list.error_code < 0) + goto fail; + + pa_op = pa_context_get_server_info(pa_ctx, pulse_server_info_cb, &dev_list); + while ((op_state = pa_operation_get_state(pa_op)) == PA_OPERATION_RUNNING) + pa_mainloop_iterate(pa_ml, 1, NULL); + if (op_state != PA_OPERATION_DONE) + dev_list.error_code = AVERROR_EXTERNAL; + pa_operation_unref(pa_op); + if (dev_list.error_code < 0) + goto fail; + + devices->default_device = -1; + for (i = 0; i < devices->nb_devices; i++) { + if (!strcmp(devices->devices[i]->device_name, dev_list.default_device)) { + devices->default_device = i; + break; + } + } + + fail: + av_free(dev_list.default_device); + if(pa_ctx) + pa_context_disconnect(pa_ctx); + if (pa_ctx) + pa_context_unref(pa_ctx); + if (pa_ml) + pa_mainloop_free(pa_ml); + return dev_list.error_code; +} diff --git a/libavdevice/pulse_audio_common.h b/libavdevice/pulse_audio_common.h index 99ba6a31f1..b049cdea1a 100644 --- a/libavdevice/pulse_audio_common.h +++ b/libavdevice/pulse_audio_common.h @@ -22,9 +22,12 @@ #ifndef AVDEVICE_PULSE_AUDIO_COMMON_H #define AVDEVICE_PULSE_AUDIO_COMMON_H -#include +#include #include "libavcodec/avcodec.h" +#include "avdevice.h" pa_sample_format_t ff_codec_id_to_pulse_format(enum AVCodecID codec_id); +int ff_pulse_audio_get_devices(AVDeviceInfoList *devices, const char *server, int output); + #endif /* AVDEVICE_PULSE_AUDIO_COMMON_H */ From 255cf03af81ee684890a83c1ddd813e0fcc9d0ad Mon Sep 17 00:00:00 2001 From: Lukasz Marek Date: Tue, 25 Feb 2014 01:04:02 +0100 Subject: [PATCH 5/6] lavd/pulse_audio_dec: implement get_device_list callback Signed-off-by: Lukasz Marek --- libavdevice/pulse_audio_dec.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libavdevice/pulse_audio_dec.c b/libavdevice/pulse_audio_dec.c index 49d6f7e7c7..ec94fe1b4a 100644 --- a/libavdevice/pulse_audio_dec.c +++ b/libavdevice/pulse_audio_dec.c @@ -147,6 +147,12 @@ static av_cold int pulse_close(AVFormatContext *s) return 0; } +static int pulse_get_device_list(AVFormatContext *h, AVDeviceInfoList *device_list) +{ + PulseData *s = h->priv_data; + return ff_pulse_audio_get_devices(device_list, s->server, 0); +} + #define OFFSET(a) offsetof(PulseData, a) #define D AV_OPT_FLAG_DECODING_PARAM @@ -176,6 +182,7 @@ AVInputFormat ff_pulse_demuxer = { .read_header = pulse_read_header, .read_packet = pulse_read_packet, .read_close = pulse_close, + .get_device_list = pulse_get_device_list, .flags = AVFMT_NOFILE, .priv_class = &pulse_demuxer_class, }; From 3937b40e87c92993df6c62492c59f59cbeb97126 Mon Sep 17 00:00:00 2001 From: Lukasz Marek Date: Tue, 25 Feb 2014 01:04:15 +0100 Subject: [PATCH 6/6] lavd/pulse_audio_enc: implement get_device_list callback Signed-off-by: Lukasz Marek --- libavdevice/pulse_audio_enc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libavdevice/pulse_audio_enc.c b/libavdevice/pulse_audio_enc.c index 03f713cb15..fa2a7cb0c5 100644 --- a/libavdevice/pulse_audio_enc.c +++ b/libavdevice/pulse_audio_enc.c @@ -167,6 +167,12 @@ static void pulse_get_output_timestamp(AVFormatContext *h, int stream, int64_t * *dts = s->timestamp - latency; } +static int pulse_get_device_list(AVFormatContext *h, AVDeviceInfoList *device_list) +{ + PulseData *s = h->priv_data; + return ff_pulse_audio_get_devices(device_list, s->server, 1); +} + #define OFFSET(a) offsetof(PulseData, a) #define E AV_OPT_FLAG_ENCODING_PARAM @@ -199,6 +205,7 @@ AVOutputFormat ff_pulse_muxer = { .write_uncoded_frame = pulse_write_frame, .write_trailer = pulse_write_trailer, .get_output_timestamp = pulse_get_output_timestamp, + .get_device_list = pulse_get_device_list, .flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH, .priv_class = &pulse_muxer_class, };