wavpack: Pass correct sample count to format_samples()
[cmus.git] / ffmpeg.c
blobb8d08327eaedf3c941853bbd3e8aa3581a433c5a
1 /*
2 * Copyright 2007 Kevin Ko <kevin.s.ko@gmail.com>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
20 #include <stdio.h>
21 #include <ffmpeg/avcodec.h>
22 #include <ffmpeg/avformat.h>
23 #include <ffmpeg/avio.h>
25 #include "ip.h"
26 #include "xmalloc.h"
27 #include "debug.h"
28 #include "utils.h"
30 #define NUM_FFMPEG_KEYS 8
32 struct ffmpeg_input {
33 AVPacket pkt;
34 int curr_pkt_size;
35 uint8_t *curr_pkt_buf;
38 struct ffmpeg_output {
39 uint8_t *buffer;
40 uint8_t *buffer_pos; /* current buffer position */
41 int buffer_used_len;
44 struct ffmpeg_private {
45 AVCodecContext *codec_context;
46 AVFormatContext *input_context;
47 int stream_index;
49 struct ffmpeg_input *input;
50 struct ffmpeg_output *output;
53 static struct ffmpeg_input *ffmpeg_input_create(void)
55 struct ffmpeg_input *input = xnew(struct ffmpeg_input, 1);
57 if (av_new_packet(&input->pkt, 0) != 0) {
58 free(input);
59 return NULL;
61 input->curr_pkt_size = 0;
62 input->curr_pkt_buf = input->pkt.data;
63 return input;
66 static void ffmpeg_input_free(struct ffmpeg_input *input)
68 av_free_packet(&input->pkt);
71 static struct ffmpeg_output *ffmpeg_output_create(void)
73 struct ffmpeg_output *output = xnew(struct ffmpeg_output, 1);
75 output->buffer = xnew(uint8_t, AVCODEC_MAX_AUDIO_FRAME_SIZE);
76 output->buffer_pos = output->buffer;
77 output->buffer_used_len = 0;
78 return output;
81 static void ffmpeg_output_free(struct ffmpeg_output *output)
83 free(output->buffer);
84 output->buffer = NULL;
85 free(output);
88 static inline void ffmpeg_buffer_flush(struct ffmpeg_output *output)
90 output->buffer_pos = output->buffer;
91 output->buffer_used_len = 0;
94 static void ffmpeg_init(void)
96 static int inited = 0;
98 if (inited != 0)
99 return;
100 inited = 1;
102 av_log_set_level(AV_LOG_QUIET);
104 #if (LIBAVFORMAT_VERSION_INT <= ((50<<16) + (4<<8) + 0))
105 avcodec_init();
106 register_avcodec(&wmav1_decoder);
107 register_avcodec(&wmav2_decoder);
109 /* libavformat versions <= 50.4.0 have asf_init(). From SVN revision
110 * 5697->5707 of asf.c, this function was removed, preferring the use of
111 * explicit calls. Note that version 50.5.0 coincides with SVN revision
112 * 5729, so there is a window of incompatibility for revisions 5707 and 5720
113 * of asf.c.
115 asf_init();
117 /* Uncomment this for shorten (.shn) support.
118 register_avcodec(&shorten_decoder);
119 raw_init();
122 register_protocol(&file_protocol);
123 #else
124 /* We could register decoders explicitly to save memory, but we have to
125 * be careful about compatibility. */
126 av_register_all();
127 #endif
130 static int ffmpeg_open(struct input_plugin_data *ip_data)
132 struct ffmpeg_private *priv;
133 int err = 0;
134 int i;
135 int stream_index = -1;
136 AVCodec *codec;
137 AVCodecContext *cc = NULL;
138 AVFormatContext *ic;
140 ffmpeg_init();
142 do {
143 err = av_open_input_file(&ic, ip_data->filename, NULL, 0, NULL);
144 if (err < 0) {
145 d_print("av_open failed: %d\n", err);
146 err = -IP_ERROR_FILE_FORMAT;
147 break;
150 err = av_find_stream_info(ic);
151 if (err < 0) {
152 d_print("unable to find stream info: %d\n", err);
153 err = -IP_ERROR_FILE_FORMAT;
154 break;
157 for (i = 0; i < ic->nb_streams; i++) {
158 cc = ic->streams[i]->codec;
159 if (cc->codec_type == CODEC_TYPE_AUDIO) {
160 stream_index = i;
161 break;
165 if (stream_index == -1) {
166 d_print("could not find audio stream\n");
167 err = -IP_ERROR_FILE_FORMAT;
168 break;
171 codec = avcodec_find_decoder(cc->codec_id);
172 if (!codec) {
173 d_print("codec not found: %d, %s\n", cc->codec_id, cc->codec_name);
174 err = -IP_ERROR_FILE_FORMAT;
175 break;
178 if (codec->capabilities & CODEC_CAP_TRUNCATED)
179 cc->flags |= CODEC_FLAG_TRUNCATED;
181 if (avcodec_open(cc, codec) < 0) {
182 d_print("could not open codec: %d, %s\n", cc->codec_id, cc->codec_name);
183 err = -IP_ERROR_FILE_FORMAT;
184 break;
186 /* We assume below that no more errors follow. */
187 } while (0);
189 if (err < 0) {
190 /* Clean up. cc is never opened at this point. (See above assumption.) */
191 av_close_input_file(ic);
192 return err;
195 priv = xnew(struct ffmpeg_private, 1);
196 priv->codec_context = cc;
197 priv->input_context = ic;
198 priv->stream_index = stream_index;
199 priv->input = ffmpeg_input_create();
200 if (priv->input == NULL) {
201 avcodec_close(cc);
202 av_close_input_file(ic);
203 free(priv);
204 return -IP_ERROR_INTERNAL;
206 priv->output = ffmpeg_output_create();
208 ip_data->private = priv;
209 ip_data->sf = sf_rate(cc->sample_rate) | sf_channels(cc->channels) | sf_bits(16) | sf_signed(1);
210 return 0;
213 static int ffmpeg_close(struct input_plugin_data *ip_data)
215 struct ffmpeg_private *priv = ip_data->private;
217 avcodec_close(priv->codec_context);
218 av_close_input_file(priv->input_context);
219 ffmpeg_input_free(priv->input);
220 ffmpeg_output_free(priv->output);
221 free(priv);
222 ip_data->private = NULL;
223 return 0;
227 * This returns the number of bytes added to the buffer.
228 * It returns < 0 on error. 0 on EOF.
230 static int ffmpeg_fill_buffer(AVFormatContext * ic, AVCodecContext * cc, struct ffmpeg_input *input,
231 struct ffmpeg_output *output)
233 /* frame_size specifies the size of output->buffer for
234 * avcodec_decode_audio2. */
235 int frame_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
236 int len;
238 while (1) {
239 if (input->curr_pkt_size <= 0) {
240 av_free_packet(&input->pkt);
241 if (av_read_frame(ic, &input->pkt) < 0) {
242 /* Force EOF once we can read no longer. */
243 return 0;
245 input->curr_pkt_size = input->pkt.size;
246 input->curr_pkt_buf = input->pkt.data;
247 continue;
250 /* The change to avcodec_decode_audio2 occurred between
251 * 51.28.0 and 51.29.0 */
252 #if (LIBAVCODEC_VERSION_INT <= ((51<<16) + (28<<8) + 0))
253 len = avcodec_decode_audio(cc, (int16_t *)output->buffer, &frame_size,
254 input->curr_pkt_buf, input->curr_pkt_size);
255 #else
256 len = avcodec_decode_audio2(cc, (int16_t *) output->buffer, &frame_size,
257 input->curr_pkt_buf, input->curr_pkt_size);
258 #endif
259 input->curr_pkt_size -= len;
260 input->curr_pkt_buf += len;
261 if (frame_size > 0) {
262 output->buffer_pos = output->buffer;
263 output->buffer_used_len = frame_size;
264 return frame_size;
267 /* This should never get here. */
268 return -IP_ERROR_INTERNAL;
271 static int ffmpeg_read(struct input_plugin_data *ip_data, char *buffer, int count)
273 struct ffmpeg_private *priv = ip_data->private;
274 struct ffmpeg_output *output = priv->output;
275 int rc;
276 int out_size;
278 if (output->buffer_used_len == 0) {
279 rc = ffmpeg_fill_buffer(priv->input_context, priv->codec_context, priv->input, priv->output);
280 if (rc <= 0) {
281 return rc;
284 out_size = min(output->buffer_used_len, count);
285 memcpy(buffer, output->buffer_pos, out_size);
286 output->buffer_used_len -= out_size;
287 output->buffer_pos += out_size;
288 return out_size;
291 static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset)
293 struct ffmpeg_private *priv = ip_data->private;
294 AVStream *st = priv->input_context->streams[priv->stream_index];
295 int ret;
297 /* There is a bug that was fixed in ffmpeg revision 5099 that affects seeking.
298 * Apparently, the stream's timebase was not used consistently in asf.c.
299 * Prior to 5099, ASF seeking assumed seconds as inputs. There is a
300 * window of incompatibility, since avformat's version was not updated at
301 * the same time. Instead, the transition to 50.3.0 occurred at
302 * revision 5028. */
303 #if (LIBAVFORMAT_VERSION_INT < ((50<<16)+(3<<8)+0))
304 int64_t pts = (int64_t) offset;
305 #else
306 /* time base is 1/framerate */
307 int64_t pts = (int64_t) offset * st->time_base.den;
308 #endif
310 ret = av_seek_frame(priv->input_context, priv->stream_index, pts, 0);
312 if (ret < 0) {
313 return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
314 } else {
315 ffmpeg_buffer_flush(priv->output);
316 return 0;
320 /* Return new i. */
321 static int set_comment(struct keyval *comment, int i, const char *key, const char *val)
323 if (val[0] == 0) {
324 return i;
326 comment[i].key = xstrdup(key);
327 comment[i].val = xstrdup(val);
328 return i + 1;
331 static int ffmpeg_read_comments(struct input_plugin_data *ip_data, struct keyval **comments)
333 char buff[16];
334 struct ffmpeg_private *priv = ip_data->private;
335 AVFormatContext *ic = priv->input_context;
336 int i = 0;
338 *comments = xnew0(struct keyval, NUM_FFMPEG_KEYS + 1);
340 i = set_comment(*comments, i, "artist", ic->author);
341 i = set_comment(*comments, i, "album", ic->album);
342 i = set_comment(*comments, i, "title", ic->title);
343 i = set_comment(*comments, i, "genre", ic->genre);
345 if (ic->year != 0) {
346 snprintf(buff, sizeof(buff), "%d", ic->year);
347 i = set_comment(*comments, i, "date", buff);
350 if (ic->track != 0) {
351 snprintf(buff, sizeof(buff), "%d", ic->track);
352 i = set_comment(*comments, i, "tracknumber", buff);
355 return 0;
358 static int ffmpeg_duration(struct input_plugin_data *ip_data)
360 struct ffmpeg_private *priv = ip_data->private;
361 return priv->input_context->duration / 1000000L;
364 const struct input_plugin_ops ip_ops = {
365 .open = ffmpeg_open,
366 .close = ffmpeg_close,
367 .read = ffmpeg_read,
368 .seek = ffmpeg_seek,
369 .read_comments = ffmpeg_read_comments,
370 .duration = ffmpeg_duration
373 const char *const ip_extensions[] = { "wma", NULL };
374 const char *const ip_mime_types[] = { NULL };