Merge commit 'lukaszmluki/master^'

* 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>
This commit is contained in:
Michael Niedermayer 2014-03-29 16:28:28 +01:00
commit bcd5fd5346
9 changed files with 249 additions and 7 deletions

View File

@ -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.

View File

@ -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;
}

View File

@ -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 */

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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;

View File

@ -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);

View File

@ -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, \

View File

@ -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");