mirror of https://github.com/FFmpeg/FFmpeg.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
256 lines
8.5 KiB
256 lines
8.5 KiB
/* |
|
* Copyright (c) 2011 Jonathan Baldwin |
|
* |
|
* This file is part of FFmpeg. |
|
* |
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
* of this software and associated documentation files (the "Software"), to deal |
|
* in the Software without restriction, including without limitation the rights |
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
* copies of the Software, and to permit persons to whom the Software is |
|
* furnished to do so, subject to the following conditions: |
|
* |
|
* The above copyright notice and this permission notice shall be included in |
|
* all copies or substantial portions of the Software. |
|
* |
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
* THE SOFTWARE. |
|
*/ |
|
|
|
/** |
|
* @file |
|
* OpenAL 1.1 capture device for libavdevice |
|
**/ |
|
|
|
#include <AL/al.h> |
|
#include <AL/alc.h> |
|
|
|
#include "libavutil/opt.h" |
|
#include "avdevice.h" |
|
|
|
typedef struct { |
|
AVClass *class; |
|
/** OpenAL capture device context. **/ |
|
ALCdevice *device; |
|
/** The number of channels in the captured audio. **/ |
|
int channels; |
|
/** The sample rate (in Hz) of the captured audio. **/ |
|
int sample_rate; |
|
/** The sample size (in bits) of the captured audio. **/ |
|
int sample_size; |
|
/** The OpenAL sample format of the captured audio. **/ |
|
ALCenum sample_format; |
|
/** The number of bytes between two consecutive samples of the same channel/component. **/ |
|
ALCint sample_step; |
|
/** If true, print a list of capture devices on this system and exit. **/ |
|
int list_devices; |
|
} al_data; |
|
|
|
typedef struct { |
|
ALCenum al_fmt; |
|
enum CodecID codec_id; |
|
int channels; |
|
} al_format_info; |
|
|
|
#define LOWEST_AL_FORMAT FFMIN(FFMIN(AL_FORMAT_MONO8,AL_FORMAT_MONO16),FFMIN(AL_FORMAT_STEREO8,AL_FORMAT_STEREO16)) |
|
|
|
/** |
|
* Get information about an AL_FORMAT value. |
|
* @param al_fmt the AL_FORMAT value to find information about. |
|
* @return A pointer to a structure containing information about the AL_FORMAT value. |
|
*/ |
|
static inline al_format_info* get_al_format_info(ALCenum al_fmt) |
|
{ |
|
static al_format_info info_table[] = { |
|
[AL_FORMAT_MONO8-LOWEST_AL_FORMAT] = {AL_FORMAT_MONO8, CODEC_ID_PCM_U8, 1}, |
|
[AL_FORMAT_MONO16-LOWEST_AL_FORMAT] = {AL_FORMAT_MONO16, AV_NE (CODEC_ID_PCM_S16BE, CODEC_ID_PCM_S16LE), 1}, |
|
[AL_FORMAT_STEREO8-LOWEST_AL_FORMAT] = {AL_FORMAT_STEREO8, CODEC_ID_PCM_U8, 2}, |
|
[AL_FORMAT_STEREO16-LOWEST_AL_FORMAT] = {AL_FORMAT_STEREO16, AV_NE (CODEC_ID_PCM_S16BE, CODEC_ID_PCM_S16LE), 2}, |
|
}; |
|
|
|
return &info_table[al_fmt-LOWEST_AL_FORMAT]; |
|
} |
|
|
|
/** |
|
* Get the OpenAL error code, translated into an av/errno error code. |
|
* @param device The ALC device to check for errors. |
|
* @param error_msg_ret A pointer to a char* in which to return the error message, or NULL if desired. |
|
* @return The error code, or 0 if there is no error. |
|
*/ |
|
static inline int al_get_error(ALCdevice *device, const char** error_msg_ret) |
|
{ |
|
ALCenum error = alcGetError(device); |
|
if (error_msg_ret) |
|
*error_msg_ret = (const char*) alcGetString(device, error); |
|
switch (error) { |
|
case ALC_NO_ERROR: |
|
return 0; |
|
case ALC_INVALID_DEVICE: |
|
return AVERROR(ENODEV); |
|
break; |
|
case ALC_INVALID_CONTEXT: |
|
case ALC_INVALID_ENUM: |
|
case ALC_INVALID_VALUE: |
|
return AVERROR(EINVAL); |
|
break; |
|
case ALC_OUT_OF_MEMORY: |
|
return AVERROR(ENOMEM); |
|
break; |
|
default: |
|
return AVERROR(EIO); |
|
} |
|
} |
|
|
|
/** |
|
* Print out a list of OpenAL capture devices on this system. |
|
*/ |
|
static inline void print_al_capture_devices(void *log_ctx) |
|
{ |
|
const char *devices; |
|
|
|
if (!(devices = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER))) |
|
return; |
|
|
|
av_log(log_ctx, AV_LOG_INFO, "List of OpenAL capture devices on this system:\n"); |
|
|
|
for (; *devices != '\0'; devices += strlen(devices) + 1) |
|
av_log(log_ctx, AV_LOG_INFO, " %s\n", devices); |
|
} |
|
|
|
static int read_header(AVFormatContext *ctx, AVFormatParameters *ap) |
|
{ |
|
al_data *ad = ctx->priv_data; |
|
static const ALCenum sample_formats[2][2] = { |
|
{ AL_FORMAT_MONO8, AL_FORMAT_STEREO8 }, |
|
{ AL_FORMAT_MONO16, AL_FORMAT_STEREO16 } |
|
}; |
|
int error = 0; |
|
const char *error_msg; |
|
AVStream *st = NULL; |
|
AVCodecContext *codec = NULL; |
|
|
|
if (ad->list_devices) { |
|
print_al_capture_devices(ctx); |
|
return AVERROR_EXIT; |
|
} |
|
|
|
ad->sample_format = sample_formats[ad->sample_size/8-1][ad->channels-1]; |
|
|
|
/* Open device for capture */ |
|
ad->device = |
|
alcCaptureOpenDevice(ctx->filename[0] ? ctx->filename : NULL, |
|
ad->sample_rate, |
|
ad->sample_format, |
|
ad->sample_rate); /* Maximum 1 second of sample data to be read at once */ |
|
|
|
if (error = al_get_error(ad->device, &error_msg)) goto fail; |
|
|
|
/* Create stream */ |
|
if (!(st = av_new_stream(ctx, 0))) { |
|
error = AVERROR(ENOMEM); |
|
goto fail; |
|
} |
|
|
|
/* We work in microseconds */ |
|
av_set_pts_info(st, 64, 1, 1000000); |
|
|
|
/* Set codec parameters */ |
|
codec = st->codec; |
|
codec->codec_type = AVMEDIA_TYPE_AUDIO; |
|
codec->sample_rate = ad->sample_rate; |
|
codec->channels = get_al_format_info(ad->sample_format)->channels; |
|
codec->codec_id = get_al_format_info(ad->sample_format)->codec_id; |
|
|
|
/* This is needed to read the audio data */ |
|
ad->sample_step = (av_get_bits_per_sample(get_al_format_info(ad->sample_format)->codec_id) * |
|
get_al_format_info(ad->sample_format)->channels) / 8; |
|
|
|
/* Finally, start the capture process */ |
|
alcCaptureStart(ad->device); |
|
|
|
return 0; |
|
|
|
fail: |
|
/* Handle failure */ |
|
if (ad->device) |
|
alcCaptureCloseDevice(ad->device); |
|
if (error_msg) |
|
av_log(ctx, AV_LOG_ERROR, "Cannot open device: %s\n", error_msg); |
|
return error; |
|
} |
|
|
|
static int read_packet(AVFormatContext* ctx, AVPacket *pkt) |
|
{ |
|
al_data *ad = ctx->priv_data; |
|
int error=0; |
|
const char *error_msg; |
|
ALCint nb_samples; |
|
|
|
/* Get number of samples available */ |
|
alcGetIntegerv(ad->device, ALC_CAPTURE_SAMPLES, (ALCsizei) sizeof(ALCint), &nb_samples); |
|
if (error = al_get_error(ad->device, &error_msg)) goto fail; |
|
|
|
/* Create a packet of appropriate size */ |
|
av_new_packet(pkt, nb_samples*ad->sample_step); |
|
pkt->pts = av_gettime(); |
|
|
|
/* Fill the packet with the available samples */ |
|
alcCaptureSamples(ad->device, pkt->data, nb_samples); |
|
if (error = al_get_error(ad->device, &error_msg)) goto fail; |
|
|
|
return pkt->size; |
|
fail: |
|
/* Handle failure */ |
|
if (pkt->data) |
|
av_destruct_packet(pkt); |
|
if (error_msg) |
|
av_log(ctx, AV_LOG_ERROR, "Error: %s\n", error_msg); |
|
return error; |
|
} |
|
|
|
static int read_close(AVFormatContext* ctx) |
|
{ |
|
al_data *ad = ctx->priv_data; |
|
|
|
if (ad->device) { |
|
alcCaptureStop(ad->device); |
|
alcCaptureCloseDevice(ad->device); |
|
} |
|
return 0; |
|
} |
|
|
|
#define OFFSET(x) offsetof(al_data, x) |
|
|
|
static const AVOption options[] = { |
|
{"channels", "set number of channels", OFFSET(channels), FF_OPT_TYPE_INT, {.dbl=2}, 1, 2, AV_OPT_FLAG_DECODING_PARAM }, |
|
{"sample_rate", "set sample rate", OFFSET(sample_rate), FF_OPT_TYPE_INT, {.dbl=44100}, 1, 192000, AV_OPT_FLAG_DECODING_PARAM }, |
|
{"sample_size", "set sample size", OFFSET(sample_size), FF_OPT_TYPE_INT, {.dbl=16}, 8, 16, AV_OPT_FLAG_DECODING_PARAM }, |
|
{"list_devices", "list available devices", OFFSET(list_devices), FF_OPT_TYPE_INT, {.dbl=0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, |
|
{"true", "", 0, FF_OPT_TYPE_CONST, {.dbl=1}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "list_devices" }, |
|
{"false", "", 0, FF_OPT_TYPE_CONST, {.dbl=0}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "list_devices" }, |
|
{NULL}, |
|
}; |
|
|
|
static const AVClass class = { |
|
.class_name = "openal", |
|
.item_name = av_default_item_name, |
|
.option = options, |
|
.version = LIBAVUTIL_VERSION_INT |
|
}; |
|
|
|
AVInputFormat ff_openal_demuxer = { |
|
.name = "openal", |
|
.long_name = NULL_IF_CONFIG_SMALL("OpenAL audio capture device"), |
|
.priv_data_size = sizeof(al_data), |
|
.read_probe = NULL, |
|
.read_header = read_header, |
|
.read_packet = read_packet, |
|
.read_close = read_close, |
|
.flags = AVFMT_NOFILE, |
|
.priv_class = &class |
|
};
|
|
|