Handle streams separately in tree_add_track()
[cmus.git] / ffmpeg.c
bloba81d0701170d41802a4223bc3c89f0c5b1156c29
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>
22 #ifdef HAVE_FFMPEG_AVCODEC_H
23 #include <ffmpeg/avcodec.h>
24 #include <ffmpeg/avformat.h>
25 #include <ffmpeg/avio.h>
26 #else
27 #include <libavcodec/avcodec.h>
28 #include <libavformat/avformat.h>
29 #include <libavformat/avio.h>
30 #endif
32 #include "ip.h"
33 #include "xmalloc.h"
34 #include "debug.h"
35 #include "utils.h"
37 #define NUM_FFMPEG_KEYS 8
39 struct ffmpeg_input {
40 AVPacket pkt;
41 int curr_pkt_size;
42 uint8_t *curr_pkt_buf;
45 struct ffmpeg_output {
46 uint8_t *buffer;
47 uint8_t *buffer_pos; /* current buffer position */
48 int buffer_used_len;
51 struct ffmpeg_private {
52 AVCodecContext *codec_context;
53 AVFormatContext *input_context;
54 int stream_index;
56 struct ffmpeg_input *input;
57 struct ffmpeg_output *output;
60 static struct ffmpeg_input *ffmpeg_input_create(void)
62 struct ffmpeg_input *input = xnew(struct ffmpeg_input, 1);
64 if (av_new_packet(&input->pkt, 0) != 0) {
65 free(input);
66 return NULL;
68 input->curr_pkt_size = 0;
69 input->curr_pkt_buf = input->pkt.data;
70 return input;
73 static void ffmpeg_input_free(struct ffmpeg_input *input)
75 av_free_packet(&input->pkt);
78 static struct ffmpeg_output *ffmpeg_output_create(void)
80 struct ffmpeg_output *output = xnew(struct ffmpeg_output, 1);
82 output->buffer = xnew(uint8_t, AVCODEC_MAX_AUDIO_FRAME_SIZE);
83 output->buffer_pos = output->buffer;
84 output->buffer_used_len = 0;
85 return output;
88 static void ffmpeg_output_free(struct ffmpeg_output *output)
90 free(output->buffer);
91 output->buffer = NULL;
92 free(output);
95 static inline void ffmpeg_buffer_flush(struct ffmpeg_output *output)
97 output->buffer_pos = output->buffer;
98 output->buffer_used_len = 0;
101 static void ffmpeg_init(void)
103 static int inited = 0;
105 if (inited != 0)
106 return;
107 inited = 1;
109 av_log_set_level(AV_LOG_QUIET);
111 #if (LIBAVFORMAT_VERSION_INT <= ((50<<16) + (4<<8) + 0))
112 avcodec_init();
113 register_avcodec(&wmav1_decoder);
114 register_avcodec(&wmav2_decoder);
116 /* libavformat versions <= 50.4.0 have asf_init(). From SVN revision
117 * 5697->5707 of asf.c, this function was removed, preferring the use of
118 * explicit calls. Note that version 50.5.0 coincides with SVN revision
119 * 5729, so there is a window of incompatibility for revisions 5707 and 5720
120 * of asf.c.
122 asf_init();
124 /* Uncomment this for shorten (.shn) support.
125 register_avcodec(&shorten_decoder);
126 raw_init();
129 register_protocol(&file_protocol);
130 #else
131 /* We could register decoders explicitly to save memory, but we have to
132 * be careful about compatibility. */
133 av_register_all();
134 #endif
137 static int ffmpeg_open(struct input_plugin_data *ip_data)
139 struct ffmpeg_private *priv;
140 int err = 0;
141 int i;
142 int stream_index = -1;
143 AVCodec *codec;
144 AVCodecContext *cc = NULL;
145 AVFormatContext *ic;
147 ffmpeg_init();
149 do {
150 err = av_open_input_file(&ic, ip_data->filename, NULL, 0, NULL);
151 if (err < 0) {
152 d_print("av_open failed: %d\n", err);
153 err = -IP_ERROR_FILE_FORMAT;
154 break;
157 err = av_find_stream_info(ic);
158 if (err < 0) {
159 d_print("unable to find stream info: %d\n", err);
160 err = -IP_ERROR_FILE_FORMAT;
161 break;
164 for (i = 0; i < ic->nb_streams; i++) {
165 cc = ic->streams[i]->codec;
166 if (cc->codec_type == CODEC_TYPE_AUDIO) {
167 stream_index = i;
168 break;
172 if (stream_index == -1) {
173 d_print("could not find audio stream\n");
174 err = -IP_ERROR_FILE_FORMAT;
175 break;
178 codec = avcodec_find_decoder(cc->codec_id);
179 if (!codec) {
180 d_print("codec not found: %d, %s\n", cc->codec_id, cc->codec_name);
181 err = -IP_ERROR_FILE_FORMAT;
182 break;
185 if (codec->capabilities & CODEC_CAP_TRUNCATED)
186 cc->flags |= CODEC_FLAG_TRUNCATED;
188 if (avcodec_open(cc, codec) < 0) {
189 d_print("could not open codec: %d, %s\n", cc->codec_id, cc->codec_name);
190 err = -IP_ERROR_FILE_FORMAT;
191 break;
193 /* We assume below that no more errors follow. */
194 } while (0);
196 if (err < 0) {
197 /* Clean up. cc is never opened at this point. (See above assumption.) */
198 av_close_input_file(ic);
199 return err;
202 priv = xnew(struct ffmpeg_private, 1);
203 priv->codec_context = cc;
204 priv->input_context = ic;
205 priv->stream_index = stream_index;
206 priv->input = ffmpeg_input_create();
207 if (priv->input == NULL) {
208 avcodec_close(cc);
209 av_close_input_file(ic);
210 free(priv);
211 return -IP_ERROR_INTERNAL;
213 priv->output = ffmpeg_output_create();
215 ip_data->private = priv;
216 ip_data->sf = sf_rate(cc->sample_rate) | sf_channels(cc->channels) | sf_bits(16) | sf_signed(1);
217 return 0;
220 static int ffmpeg_close(struct input_plugin_data *ip_data)
222 struct ffmpeg_private *priv = ip_data->private;
224 avcodec_close(priv->codec_context);
225 av_close_input_file(priv->input_context);
226 ffmpeg_input_free(priv->input);
227 ffmpeg_output_free(priv->output);
228 free(priv);
229 ip_data->private = NULL;
230 return 0;
234 * This returns the number of bytes added to the buffer.
235 * It returns < 0 on error. 0 on EOF.
237 static int ffmpeg_fill_buffer(AVFormatContext *ic, AVCodecContext *cc, struct ffmpeg_input *input,
238 struct ffmpeg_output *output)
240 /* frame_size specifies the size of output->buffer for
241 * avcodec_decode_audio2. */
242 int frame_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
243 int len;
245 while (1) {
246 if (input->curr_pkt_size <= 0) {
247 av_free_packet(&input->pkt);
248 if (av_read_frame(ic, &input->pkt) < 0) {
249 /* Force EOF once we can read no longer. */
250 return 0;
252 input->curr_pkt_size = input->pkt.size;
253 input->curr_pkt_buf = input->pkt.data;
254 continue;
257 /* The change to avcodec_decode_audio2 occurred between
258 * 51.28.0 and 51.29.0 */
259 #if (LIBAVCODEC_VERSION_INT <= ((51<<16) + (28<<8) + 0))
260 len = avcodec_decode_audio(cc, (int16_t *)output->buffer, &frame_size,
261 input->curr_pkt_buf, input->curr_pkt_size);
262 #else
263 len = avcodec_decode_audio2(cc, (int16_t *) output->buffer, &frame_size,
264 input->curr_pkt_buf, input->curr_pkt_size);
265 #endif
266 input->curr_pkt_size -= len;
267 input->curr_pkt_buf += len;
268 if (frame_size > 0) {
269 output->buffer_pos = output->buffer;
270 output->buffer_used_len = frame_size;
271 return frame_size;
274 /* This should never get here. */
275 return -IP_ERROR_INTERNAL;
278 static int ffmpeg_read(struct input_plugin_data *ip_data, char *buffer, int count)
280 struct ffmpeg_private *priv = ip_data->private;
281 struct ffmpeg_output *output = priv->output;
282 int rc;
283 int out_size;
285 if (output->buffer_used_len == 0) {
286 rc = ffmpeg_fill_buffer(priv->input_context, priv->codec_context, priv->input, priv->output);
287 if (rc <= 0) {
288 return rc;
291 out_size = min(output->buffer_used_len, count);
292 memcpy(buffer, output->buffer_pos, out_size);
293 output->buffer_used_len -= out_size;
294 output->buffer_pos += out_size;
295 return out_size;
298 static int ffmpeg_seek(struct input_plugin_data *ip_data, double offset)
300 struct ffmpeg_private *priv = ip_data->private;
301 AVStream *st = priv->input_context->streams[priv->stream_index];
302 int ret;
304 /* There is a bug that was fixed in ffmpeg revision 5099 that affects seeking.
305 * Apparently, the stream's timebase was not used consistently in asf.c.
306 * Prior to 5099, ASF seeking assumed seconds as inputs. There is a
307 * window of incompatibility, since avformat's version was not updated at
308 * the same time. Instead, the transition to 50.3.0 occurred at
309 * revision 5028. */
310 #if (LIBAVFORMAT_VERSION_INT < ((50<<16)+(3<<8)+0))
311 int64_t pts = (int64_t) offset;
312 #else
313 /* time base is 1/framerate */
314 int64_t pts = (int64_t) offset * st->time_base.den;
315 #endif
317 ret = av_seek_frame(priv->input_context, priv->stream_index, pts, 0);
319 if (ret < 0) {
320 return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
321 } else {
322 ffmpeg_buffer_flush(priv->output);
323 return 0;
327 /* Return new i. */
328 static int set_comment(struct keyval *comment, int i, const char *key, const char *val)
330 if (val[0] == 0) {
331 return i;
333 comment[i].key = xstrdup(key);
334 comment[i].val = xstrdup(val);
335 return i + 1;
338 static int ffmpeg_read_comments(struct input_plugin_data *ip_data, struct keyval **comments)
340 char buff[16];
341 struct ffmpeg_private *priv = ip_data->private;
342 AVFormatContext *ic = priv->input_context;
343 int i = 0;
345 *comments = xnew0(struct keyval, NUM_FFMPEG_KEYS + 1);
347 i = set_comment(*comments, i, "artist", ic->author);
348 i = set_comment(*comments, i, "album", ic->album);
349 i = set_comment(*comments, i, "title", ic->title);
350 i = set_comment(*comments, i, "genre", ic->genre);
352 if (ic->year != 0) {
353 snprintf(buff, sizeof(buff), "%d", ic->year);
354 i = set_comment(*comments, i, "date", buff);
357 if (ic->track != 0) {
358 snprintf(buff, sizeof(buff), "%d", ic->track);
359 i = set_comment(*comments, i, "tracknumber", buff);
362 return 0;
365 static int ffmpeg_duration(struct input_plugin_data *ip_data)
367 struct ffmpeg_private *priv = ip_data->private;
368 return priv->input_context->duration / 1000000L;
371 const struct input_plugin_ops ip_ops = {
372 .open = ffmpeg_open,
373 .close = ffmpeg_close,
374 .read = ffmpeg_read,
375 .seek = ffmpeg_seek,
376 .read_comments = ffmpeg_read_comments,
377 .duration = ffmpeg_duration
380 const char *const ip_extensions[] = { "wma", NULL };
381 const char *const ip_mime_types[] = { NULL };