Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * Copyright (c) 2011 Jonathan Baldwin | |
3 | * | |
4 | * This file is part of FFmpeg. | |
5 | * | |
6 | * Permission to use, copy, modify, and/or distribute this software for any | |
7 | * purpose with or without fee is hereby granted, provided that the above | |
8 | * copyright notice and this permission notice appear in all copies. | |
9 | * | |
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | |
11 | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | |
12 | * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | |
13 | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | |
14 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | |
15 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | |
16 | * PERFORMANCE OF THIS SOFTWARE. | |
17 | */ | |
18 | ||
19 | /** | |
20 | * @file | |
21 | * OpenAL 1.1 capture device for libavdevice | |
22 | **/ | |
23 | ||
24 | #include <AL/al.h> | |
25 | #include <AL/alc.h> | |
26 | ||
27 | #include "libavutil/opt.h" | |
28 | #include "libavutil/time.h" | |
29 | #include "libavformat/internal.h" | |
30 | #include "avdevice.h" | |
31 | ||
32 | typedef struct { | |
33 | AVClass *class; | |
34 | /** OpenAL capture device context. **/ | |
35 | ALCdevice *device; | |
36 | /** The number of channels in the captured audio. **/ | |
37 | int channels; | |
38 | /** The sample rate (in Hz) of the captured audio. **/ | |
39 | int sample_rate; | |
40 | /** The sample size (in bits) of the captured audio. **/ | |
41 | int sample_size; | |
42 | /** The OpenAL sample format of the captured audio. **/ | |
43 | ALCenum sample_format; | |
44 | /** The number of bytes between two consecutive samples of the same channel/component. **/ | |
45 | ALCint sample_step; | |
46 | /** If true, print a list of capture devices on this system and exit. **/ | |
47 | int list_devices; | |
48 | } al_data; | |
49 | ||
50 | typedef struct { | |
51 | ALCenum al_fmt; | |
52 | enum AVCodecID codec_id; | |
53 | int channels; | |
54 | } al_format_info; | |
55 | ||
56 | #define LOWEST_AL_FORMAT FFMIN(FFMIN(AL_FORMAT_MONO8,AL_FORMAT_MONO16),FFMIN(AL_FORMAT_STEREO8,AL_FORMAT_STEREO16)) | |
57 | ||
58 | /** | |
59 | * Get information about an AL_FORMAT value. | |
60 | * @param al_fmt the AL_FORMAT value to find information about. | |
61 | * @return A pointer to a structure containing information about the AL_FORMAT value. | |
62 | */ | |
63 | static inline al_format_info* get_al_format_info(ALCenum al_fmt) | |
64 | { | |
65 | static al_format_info info_table[] = { | |
66 | [AL_FORMAT_MONO8-LOWEST_AL_FORMAT] = {AL_FORMAT_MONO8, AV_CODEC_ID_PCM_U8, 1}, | |
67 | [AL_FORMAT_MONO16-LOWEST_AL_FORMAT] = {AL_FORMAT_MONO16, AV_NE (AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), 1}, | |
68 | [AL_FORMAT_STEREO8-LOWEST_AL_FORMAT] = {AL_FORMAT_STEREO8, AV_CODEC_ID_PCM_U8, 2}, | |
69 | [AL_FORMAT_STEREO16-LOWEST_AL_FORMAT] = {AL_FORMAT_STEREO16, AV_NE (AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), 2}, | |
70 | }; | |
71 | ||
72 | return &info_table[al_fmt-LOWEST_AL_FORMAT]; | |
73 | } | |
74 | ||
75 | /** | |
76 | * Get the OpenAL error code, translated into an av/errno error code. | |
77 | * @param device The ALC device to check for errors. | |
78 | * @param error_msg_ret A pointer to a char* in which to return the error message, or NULL if desired. | |
79 | * @return The error code, or 0 if there is no error. | |
80 | */ | |
81 | static inline int al_get_error(ALCdevice *device, const char** error_msg_ret) | |
82 | { | |
83 | ALCenum error = alcGetError(device); | |
84 | if (error_msg_ret) | |
85 | *error_msg_ret = (const char*) alcGetString(device, error); | |
86 | switch (error) { | |
87 | case ALC_NO_ERROR: | |
88 | return 0; | |
89 | case ALC_INVALID_DEVICE: | |
90 | return AVERROR(ENODEV); | |
91 | break; | |
92 | case ALC_INVALID_CONTEXT: | |
93 | case ALC_INVALID_ENUM: | |
94 | case ALC_INVALID_VALUE: | |
95 | return AVERROR(EINVAL); | |
96 | break; | |
97 | case ALC_OUT_OF_MEMORY: | |
98 | return AVERROR(ENOMEM); | |
99 | break; | |
100 | default: | |
101 | return AVERROR(EIO); | |
102 | } | |
103 | } | |
104 | ||
105 | /** | |
106 | * Print out a list of OpenAL capture devices on this system. | |
107 | */ | |
108 | static inline void print_al_capture_devices(void *log_ctx) | |
109 | { | |
110 | const char *devices; | |
111 | ||
112 | if (!(devices = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER))) | |
113 | return; | |
114 | ||
115 | av_log(log_ctx, AV_LOG_INFO, "List of OpenAL capture devices on this system:\n"); | |
116 | ||
117 | for (; *devices != '\0'; devices += strlen(devices) + 1) | |
118 | av_log(log_ctx, AV_LOG_INFO, " %s\n", devices); | |
119 | } | |
120 | ||
121 | static int read_header(AVFormatContext *ctx) | |
122 | { | |
123 | al_data *ad = ctx->priv_data; | |
124 | static const ALCenum sample_formats[2][2] = { | |
125 | { AL_FORMAT_MONO8, AL_FORMAT_STEREO8 }, | |
126 | { AL_FORMAT_MONO16, AL_FORMAT_STEREO16 } | |
127 | }; | |
128 | int error = 0; | |
129 | const char *error_msg; | |
130 | AVStream *st = NULL; | |
131 | AVCodecContext *codec = NULL; | |
132 | ||
133 | if (ad->list_devices) { | |
134 | print_al_capture_devices(ctx); | |
135 | return AVERROR_EXIT; | |
136 | } | |
137 | ||
138 | ad->sample_format = sample_formats[ad->sample_size/8-1][ad->channels-1]; | |
139 | ||
140 | /* Open device for capture */ | |
141 | ad->device = | |
142 | alcCaptureOpenDevice(ctx->filename[0] ? ctx->filename : NULL, | |
143 | ad->sample_rate, | |
144 | ad->sample_format, | |
145 | ad->sample_rate); /* Maximum 1 second of sample data to be read at once */ | |
146 | ||
147 | if (error = al_get_error(ad->device, &error_msg)) goto fail; | |
148 | ||
149 | /* Create stream */ | |
150 | if (!(st = avformat_new_stream(ctx, NULL))) { | |
151 | error = AVERROR(ENOMEM); | |
152 | goto fail; | |
153 | } | |
154 | ||
155 | /* We work in microseconds */ | |
156 | avpriv_set_pts_info(st, 64, 1, 1000000); | |
157 | ||
158 | /* Set codec parameters */ | |
159 | codec = st->codec; | |
160 | codec->codec_type = AVMEDIA_TYPE_AUDIO; | |
161 | codec->sample_rate = ad->sample_rate; | |
162 | codec->channels = get_al_format_info(ad->sample_format)->channels; | |
163 | codec->codec_id = get_al_format_info(ad->sample_format)->codec_id; | |
164 | ||
165 | /* This is needed to read the audio data */ | |
166 | ad->sample_step = (av_get_bits_per_sample(get_al_format_info(ad->sample_format)->codec_id) * | |
167 | get_al_format_info(ad->sample_format)->channels) / 8; | |
168 | ||
169 | /* Finally, start the capture process */ | |
170 | alcCaptureStart(ad->device); | |
171 | ||
172 | return 0; | |
173 | ||
174 | fail: | |
175 | /* Handle failure */ | |
176 | if (ad->device) | |
177 | alcCaptureCloseDevice(ad->device); | |
178 | if (error_msg) | |
179 | av_log(ctx, AV_LOG_ERROR, "Cannot open device: %s\n", error_msg); | |
180 | return error; | |
181 | } | |
182 | ||
183 | static int read_packet(AVFormatContext* ctx, AVPacket *pkt) | |
184 | { | |
185 | al_data *ad = ctx->priv_data; | |
186 | int error=0; | |
187 | const char *error_msg; | |
188 | ALCint nb_samples; | |
189 | ||
190 | /* Get number of samples available */ | |
191 | alcGetIntegerv(ad->device, ALC_CAPTURE_SAMPLES, (ALCsizei) sizeof(ALCint), &nb_samples); | |
192 | if (error = al_get_error(ad->device, &error_msg)) goto fail; | |
193 | ||
194 | /* Create a packet of appropriate size */ | |
195 | av_new_packet(pkt, nb_samples*ad->sample_step); | |
196 | pkt->pts = av_gettime(); | |
197 | ||
198 | /* Fill the packet with the available samples */ | |
199 | alcCaptureSamples(ad->device, pkt->data, nb_samples); | |
200 | if (error = al_get_error(ad->device, &error_msg)) goto fail; | |
201 | ||
202 | return pkt->size; | |
203 | fail: | |
204 | /* Handle failure */ | |
205 | if (pkt->data) | |
206 | av_destruct_packet(pkt); | |
207 | if (error_msg) | |
208 | av_log(ctx, AV_LOG_ERROR, "Error: %s\n", error_msg); | |
209 | return error; | |
210 | } | |
211 | ||
212 | static int read_close(AVFormatContext* ctx) | |
213 | { | |
214 | al_data *ad = ctx->priv_data; | |
215 | ||
216 | if (ad->device) { | |
217 | alcCaptureStop(ad->device); | |
218 | alcCaptureCloseDevice(ad->device); | |
219 | } | |
220 | return 0; | |
221 | } | |
222 | ||
223 | #define OFFSET(x) offsetof(al_data, x) | |
224 | ||
225 | static const AVOption options[] = { | |
226 | {"channels", "set number of channels", OFFSET(channels), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, AV_OPT_FLAG_DECODING_PARAM }, | |
227 | {"sample_rate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, 192000, AV_OPT_FLAG_DECODING_PARAM }, | |
228 | {"sample_size", "set sample size", OFFSET(sample_size), AV_OPT_TYPE_INT, {.i64=16}, 8, 16, AV_OPT_FLAG_DECODING_PARAM }, | |
229 | {"list_devices", "list available devices", OFFSET(list_devices), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM, "list_devices" }, | |
230 | {"true", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "list_devices" }, | |
231 | {"false", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "list_devices" }, | |
232 | {NULL}, | |
233 | }; | |
234 | ||
235 | static const AVClass class = { | |
236 | .class_name = "openal", | |
237 | .item_name = av_default_item_name, | |
238 | .option = options, | |
239 | .version = LIBAVUTIL_VERSION_INT, | |
240 | .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT, | |
241 | }; | |
242 | ||
243 | AVInputFormat ff_openal_demuxer = { | |
244 | .name = "openal", | |
245 | .long_name = NULL_IF_CONFIG_SMALL("OpenAL audio capture device"), | |
246 | .priv_data_size = sizeof(al_data), | |
247 | .read_probe = NULL, | |
248 | .read_header = read_header, | |
249 | .read_packet = read_packet, | |
250 | .read_close = read_close, | |
251 | .flags = AVFMT_NOFILE, | |
252 | .priv_class = &class | |
253 | }; |