* commit 'lukaszmluki/master^': lavd/pulse_audio_enc: implement get_device_list callback lavd/pulse_audio_dec: implement get_device_list callback lavd/pulse_audio_common: add device detecting code lavu/mem: add av_dynarray_add_nofree function lavd/pulse_audio_enc: implement write_uncoded_frame callback tools/uncoded_frame: fix audio codec generation Merged-by: Michael Niedermayer <michaelni@gmx.at>tags/n2.3
| @@ -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. | |||
| @@ -1,5 +1,6 @@ | |||
| /* | |||
| * Pulseaudio input | |||
| * Pulseaudio common | |||
| * Copyright (c) 2014 Lukasz Marek | |||
| * Copyright (c) 2011 Luca Barbato <lu_zero@gentoo.org> | |||
| * | |||
| * 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; | |||
| } | |||
| @@ -22,9 +22,12 @@ | |||
| #ifndef AVDEVICE_PULSE_AUDIO_COMMON_H | |||
| #define AVDEVICE_PULSE_AUDIO_COMMON_H | |||
| #include <pulse/simple.h> | |||
| #include <pulse/pulseaudio.h> | |||
| #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 */ | |||
| @@ -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, | |||
| }; | |||
| @@ -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; | |||
| @@ -149,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 | |||
| @@ -178,8 +202,10 @@ 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, | |||
| .get_device_list = pulse_get_device_list, | |||
| .flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH, | |||
| .priv_class = &pulse_muxer_class, | |||
| }; | |||
| @@ -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; | |||
| @@ -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); | |||
| @@ -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, \ | |||
| @@ -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"); | |||