mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-11-21 10:55:51 +02:00
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:
commit
bcd5fd5346
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user