ffmpeg_plugin.c 11.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* the Music Player Daemon (MPD)
 * Copyright (C) 2008 Viliam Mateicka <viliam.mateicka@gmail.com>
 * This project's homepage is: http://www.musicpd.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "../decoder_api.h"
20
#include "config.h"
21

22 23
#include <glib.h>

24
#include <assert.h>
25 26 27 28 29 30 31 32
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

33
#ifdef OLD_FFMPEG_INCLUDES
34 35 36
#include <avcodec.h>
#include <avformat.h>
#include <avio.h>
37 38 39 40 41
#else
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#endif
42

43 44 45
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "ffmpeg"

Max Kellermann's avatar
Max Kellermann committed
46 47 48 49
struct ffmpeg_context {
	int audio_stream;
	AVFormatContext *format_context;
	AVCodecContext *codec_context;
50
	struct decoder *decoder;
51
	struct input_stream *input;
52
	struct tag *tag;
Max Kellermann's avatar
Max Kellermann committed
53
};
54

Max Kellermann's avatar
Max Kellermann committed
55 56
struct ffmpeg_stream {
	/** hack - see url_to_struct() */
57 58
	char url[8];

59
	struct decoder *decoder;
60
	struct input_stream *input;
Max Kellermann's avatar
Max Kellermann committed
61
};
62

63
/**
Max Kellermann's avatar
Max Kellermann committed
64
 * Convert a faked mpd:// URL to a ffmpeg_stream structure.  This is a
65 66 67
 * hack because ffmpeg does not provide a nice API for passing a
 * user-defined pointer to mpdurl_open().
 */
Max Kellermann's avatar
Max Kellermann committed
68
static struct ffmpeg_stream *url_to_struct(const char *url)
69
{
70 71
	union {
		const char *in;
Max Kellermann's avatar
Max Kellermann committed
72
		struct ffmpeg_stream *out;
73 74 75 76
	} u = { .in = url };
	return u.out;
}

Max Kellermann's avatar
Max Kellermann committed
77
static int mpd_ffmpeg_open(URLContext *h, const char *filename,
78
			   G_GNUC_UNUSED int flags)
79
{
Max Kellermann's avatar
Max Kellermann committed
80 81 82
	struct ffmpeg_stream *stream = url_to_struct(filename);
	h->priv_data = stream;
	h->is_streamed = stream->input->seekable ? 0 : 1;
83
	return 0;
84 85
}

Max Kellermann's avatar
Max Kellermann committed
86
static int mpd_ffmpeg_read(URLContext *h, unsigned char *buf, int size)
87
{
Max Kellermann's avatar
Max Kellermann committed
88
	struct ffmpeg_stream *stream = (struct ffmpeg_stream *) h->priv_data;
89

90 91
	return decoder_read(stream->decoder, stream->input,
			    (void *)buf, size);
92 93
}

Max Kellermann's avatar
Max Kellermann committed
94
static int64_t mpd_ffmpeg_seek(URLContext *h, int64_t pos, int whence)
95
{
Max Kellermann's avatar
Max Kellermann committed
96
	struct ffmpeg_stream *stream = (struct ffmpeg_stream *) h->priv_data;
97 98 99 100 101 102 103 104 105
	bool ret;

	if (whence == AVSEEK_SIZE)
		return stream->input->size;

	ret = input_stream_seek(stream->input, pos, whence);
	if (!ret)
		return -1;

Max Kellermann's avatar
Max Kellermann committed
106
	return stream->input->offset;
107 108
}

Max Kellermann's avatar
Max Kellermann committed
109
static int mpd_ffmpeg_close(URLContext *h)
110
{
111
	h->priv_data = NULL;
112 113 114
	return 0;
}

Max Kellermann's avatar
Max Kellermann committed
115
static URLProtocol mpd_ffmpeg_fileops = {
116
	.name = "mpd",
Max Kellermann's avatar
Max Kellermann committed
117 118 119 120
	.url_open = mpd_ffmpeg_open,
	.url_read = mpd_ffmpeg_read,
	.url_seek = mpd_ffmpeg_seek,
	.url_close = mpd_ffmpeg_close,
121 122
};

123 124
static bool
ffmpeg_init(G_GNUC_UNUSED const struct config_param *param)
125 126
{
	av_register_all();
Max Kellermann's avatar
Max Kellermann committed
127
	register_protocol(&mpd_ffmpeg_fileops);
128
	return true;
129 130
}

131 132 133 134 135 136 137 138 139 140 141
static int
ffmpeg_find_audio_stream(const AVFormatContext *format_context)
{
	for (unsigned i = 0; i < format_context->nb_streams; ++i)
		if (format_context->streams[i]->codec->codec_type ==
		    CODEC_TYPE_AUDIO)
			return i;

	return -1;
}

142
static bool
Max Kellermann's avatar
Max Kellermann committed
143 144 145
ffmpeg_helper(struct input_stream *input,
	      bool (*callback)(struct ffmpeg_context *ctx),
	      struct ffmpeg_context *ctx)
146
{
Max Kellermann's avatar
Max Kellermann committed
147 148 149 150 151
	AVFormatContext *format_context;
	AVCodecContext *codec_context;
	AVCodec *codec;
	int audio_stream;
	struct ffmpeg_stream stream = {
152 153
		.url = "mpd://X", /* only the mpd:// prefix matters */
	};
154
	bool ret;
155

Max Kellermann's avatar
Max Kellermann committed
156 157 158
	stream.input = input;
	if (ctx && ctx->decoder) {
		stream.decoder = ctx->decoder; //are we in decoding loop ?
159
	} else {
Max Kellermann's avatar
Max Kellermann committed
160
		stream.decoder = NULL;
161 162 163
	}

	//ffmpeg works with ours "fileops" helper
Max Kellermann's avatar
Max Kellermann committed
164
	if (av_open_input_file(&format_context, stream.url, NULL, 0, NULL) != 0) {
165
		g_warning("Open failed\n");
166
		return false;
167 168
	}

Max Kellermann's avatar
Max Kellermann committed
169
	if (av_find_stream_info(format_context)<0) {
170
		g_warning("Couldn't find stream info\n");
171
		return false;
172 173
	}

174
	audio_stream = ffmpeg_find_audio_stream(format_context);
Max Kellermann's avatar
Max Kellermann committed
175
	if (audio_stream == -1) {
176
		g_warning("No audio stream inside\n");
177
		return false;
178 179
	}

Max Kellermann's avatar
Max Kellermann committed
180
	codec_context = format_context->streams[audio_stream]->codec;
181 182 183
	if (codec_context->codec_name[0] != 0)
		g_debug("codec '%s'", codec_context->codec_name);

Max Kellermann's avatar
Max Kellermann committed
184
	codec = avcodec_find_decoder(codec_context->codec_id);
185

Max Kellermann's avatar
Max Kellermann committed
186
	if (!codec) {
187
		g_warning("Unsupported audio codec\n");
188
		return false;
189 190
	}

Max Kellermann's avatar
Max Kellermann committed
191
	if (avcodec_open(codec_context, codec)<0) {
192
		g_warning("Could not open codec\n");
193
		return false;
194 195 196
	}

	if (callback) {
Max Kellermann's avatar
Max Kellermann committed
197 198 199
		ctx->audio_stream = audio_stream;
		ctx->format_context = format_context;
		ctx->codec_context = codec_context;
200

Max Kellermann's avatar
Max Kellermann committed
201
		ret = callback(ctx);
202
	} else
203
		ret = true;
204

Max Kellermann's avatar
Max Kellermann committed
205 206
	avcodec_close(codec_context);
	av_close_input_file(format_context);
207 208 209 210

	return ret;
}

211
static enum decoder_command
212 213
ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
		   const AVPacket *packet,
214 215 216
		   AVCodecContext *codec_context,
		   const AVRational *time_base)
{
217
	enum decoder_command cmd = DECODE_COMMAND_NONE;
218 219 220
	int position;
	uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
	int len, audio_size;
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
	uint8_t *packet_data;
	int packet_size;

	packet_data = packet->data;
	packet_size = packet->size;

	while ((packet_size > 0) && (cmd == DECODE_COMMAND_NONE)) {
		audio_size = sizeof(audio_buf);
		len = avcodec_decode_audio2(codec_context,
					    (int16_t *)audio_buf,
					    &audio_size,
					    packet_data, packet_size);

		if (len < 0) {
			/* if error, we skip the frame */
			g_message("decoding failed\n");
			break;
		}

		packet_data += len;
		packet_size -= len;

243
		if (audio_size <= 0)
244
			continue;
245

246 247 248 249 250
		position = packet->pts != (int64_t)AV_NOPTS_VALUE
			? av_rescale_q(packet->pts, *time_base,
				       (AVRational){1, 1})
			: 0;

251 252 253 254
		cmd = decoder_data(decoder, is,
				   audio_buf, audio_size,
				   position,
				   codec_context->bit_rate / 1000, NULL);
255
	}
256
	return cmd;
257 258
}

259
static bool
Max Kellermann's avatar
Max Kellermann committed
260
ffmpeg_decode_internal(struct ffmpeg_context *ctx)
261
{
Max Kellermann's avatar
Max Kellermann committed
262 263 264
	struct decoder *decoder = ctx->decoder;
	AVCodecContext *codec_context = ctx->codec_context;
	AVFormatContext *format_context = ctx->format_context;
265 266
	AVPacket packet;
	struct audio_format audio_format;
267
	enum decoder_command cmd;
268
	int total_time;
269 270 271

	total_time = 0;

Max Kellermann's avatar
Max Kellermann committed
272 273
	if (codec_context->channels > 2) {
		codec_context->channels = 2;
274 275 276
	}

	audio_format.bits = (uint8_t)16;
Max Kellermann's avatar
Max Kellermann committed
277 278
	audio_format.sample_rate = (unsigned int)codec_context->sample_rate;
	audio_format.channels = codec_context->channels;
279

280 281 282 283 284 285 286
	if (!audio_format_valid(&audio_format)) {
		g_warning("Invalid audio format: %u:%u:%u\n",
			  audio_format.sample_rate, audio_format.bits,
			  audio_format.channels);
		return false;
	}

287
	//there is some problem with this on some demux (mp3 at least)
288
	if (format_context->duration != (int64_t)AV_NOPTS_VALUE) {
Max Kellermann's avatar
Max Kellermann committed
289
		total_time = format_context->duration / AV_TIME_BASE;
290 291
	}

292
	decoder_initialized(decoder, &audio_format,
Max Kellermann's avatar
Max Kellermann committed
293
			    ctx->input->seekable, total_time);
294 295

	do {
Max Kellermann's avatar
Max Kellermann committed
296
		if (av_read_frame(format_context, &packet) < 0)
297 298 299
			/* end of file */
			break;

Max Kellermann's avatar
Max Kellermann committed
300 301 302 303
		if (packet.stream_index == ctx->audio_stream)
			cmd = ffmpeg_send_packet(decoder, ctx->input,
						 &packet, codec_context,
						 &format_context->streams[ctx->audio_stream]->time_base);
304 305 306 307
		else
			cmd = decoder_get_command(decoder);

		av_free_packet(&packet);
308

309
		if (cmd == DECODE_COMMAND_SEEK) {
310 311
			int64_t where =
				decoder_seek_where(decoder) * AV_TIME_BASE;
312

313
			if (av_seek_frame(format_context, -1, where, 0) < 0)
314 315 316
				decoder_seek_error(decoder);
			else
				decoder_command_finished(decoder);
317
		}
318
	} while (cmd != DECODE_COMMAND_STOP);
319

320
	return true;
321 322
}

323
static void
324
ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
325
{
Max Kellermann's avatar
Max Kellermann committed
326
	struct ffmpeg_context ctx;
327

Max Kellermann's avatar
Max Kellermann committed
328 329
	ctx.input = input;
	ctx.decoder = decoder;
330

331
	ffmpeg_helper(input, ffmpeg_decode_internal, &ctx);
332 333
}

Max Kellermann's avatar
Max Kellermann committed
334
static bool ffmpeg_tag_internal(struct ffmpeg_context *ctx)
335
{
Max Kellermann's avatar
Max Kellermann committed
336
	struct tag *tag = (struct tag *) ctx->tag;
337 338 339
	const AVFormatContext *f = ctx->format_context;

	tag->time = 0;
340
	if (f->duration != (int64_t)AV_NOPTS_VALUE)
341 342 343 344 345 346 347 348 349 350 351 352
		tag->time = f->duration / AV_TIME_BASE;
	if (f->author[0])
		tag_add_item(tag, TAG_ITEM_ARTIST, f->author);
	if (f->title[0])
		tag_add_item(tag, TAG_ITEM_TITLE, f->title);
	if (f->album[0])
		tag_add_item(tag, TAG_ITEM_ALBUM, f->album);

	if (f->track > 0) {
		char buffer[16];
		snprintf(buffer, sizeof(buffer), "%d", f->track);
		tag_add_item(tag, TAG_ITEM_TRACK, buffer);
353
	}
354

355 356 357 358 359 360 361 362 363 364
	if (f->comment[0])
		tag_add_item(tag, TAG_ITEM_COMMENT, f->comment);
	if (f->genre[0])
		tag_add_item(tag, TAG_ITEM_GENRE, f->genre);
	if (f->year > 0) {
		char buffer[16];
		snprintf(buffer, sizeof(buffer), "%d", f->year);
		tag_add_item(tag, TAG_ITEM_DATE, buffer);
	}

365
	return true;
366 367 368
}

//no tag reading in ffmpeg, check if playable
369
static struct tag *ffmpeg_tag(const char *file)
370
{
371
	struct input_stream input;
Max Kellermann's avatar
Max Kellermann committed
372
	struct ffmpeg_context ctx;
373
	bool ret;
374

375
	if (!input_stream_open(&input, file)) {
376
		g_warning("failed to open %s\n", file);
377 378 379
		return NULL;
	}

Max Kellermann's avatar
Max Kellermann committed
380 381
	ctx.decoder = NULL;
	ctx.tag = tag_new();
382

Max Kellermann's avatar
Max Kellermann committed
383
	ret = ffmpeg_helper(&input, ffmpeg_tag_internal, &ctx);
384
	if (!ret) {
Max Kellermann's avatar
Max Kellermann committed
385 386
		tag_free(ctx.tag);
		ctx.tag = NULL;
387 388
	}

389
	input_stream_close(&input);
390

Max Kellermann's avatar
Max Kellermann committed
391
	return ctx.tag;
392 393 394
}

/**
395 396 397 398
 * A list of extensions found for the formats supported by ffmpeg.
 * This list is current as of 02-23-09; To find out if there are more
 * supported formats, check the ffmpeg changelog since this date for
 * more formats.
399
 */
Max Kellermann's avatar
Max Kellermann committed
400
static const char *const ffmpeg_suffixes[] = {
401 402 403 404 405 406 407 408 409 410 411 412 413 414
	"16sv", "3g2", "3gp", "4xm", "8svx", "aa3", "aac", "ac3", "afc", "aif",
	"aifc", "aiff", "al", "alaw", "amr", "anim", "apc", "ape", "asf",
	"atrac", "au", "aud", "avi", "avm2", "avs", "bap", "bfi", "c93", "cak",
	"cin", "cmv", "cpk", "daud", "dct", "divx", "dts", "dv", "dvd", "dxa",
	"eac3", "film", "flac", "flc", "fli", "fll", "flx", "flv", "g726",
	"gsm", "gxf", "iss", "m1v", "m2v", "m2t", "m2ts", "m4a", "m4v", "mad",
	"mj2", "mjpeg", "mjpg", "mka", "mkv", "mlp", "mm", "mmf", "mov", "mp+",
	"mp1", "mp2", "mp3", "mp4", "mpc", "mpeg", "mpg", "mpga", "mpp", "mpu",
	"mve", "mvi", "mxf", "nc", "nsv", "nut", "nuv", "oga", "ogm", "ogv",
	"ogx", "oma", "ogg", "omg", "psp", "pva", "qcp", "qt", "r3d", "ra",
	"ram", "rl2", "rm", "rmvb", "roq", "rpl", "rvc", "shn", "smk", "snd",
	"sol", "son", "spx", "str", "swf", "tgi", "tgq", "tgv", "thp", "ts",
	"tsp", "tta", "xa", "xvid", "uv", "uv2", "vb", "vid", "vob", "voc",
	"vp6", "vmd", "wav", "wma", "wmv", "wsaud", "wsvga", "wv", "wve",
415 416 417
	NULL
};

Max Kellermann's avatar
Max Kellermann committed
418
static const char *const ffmpeg_mime_types[] = {
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
	"application/mp4",
	"application/octet-stream",
	"application/ogg",
	"application/x-ms-wmz",
	"application/x-ms-wmd",
	"application/x-shockwave-flash",
	"application/x-shorten",
	"audio/8svx",
	"audio/16sv",
	"audio/aac",
	"audio/ac3",
	"audio/amr",
	"audio/basic",
	"audio/flac",
	"audio/mpeg",
	"audio/musepack",
	"audio/ogg",
	"audio/qcelp",
	"audio/vorbis",
	"audio/x-8svx",
	"audio/x-16sv",
	"audio/x-aac",
	"audio/x-ac3",
	"audio/x-aiff"
	"audio/x-alaw",
	"audio/x-au",
	"audio/x-dca",
	"audio/x-eac3",
	"audio/x-flac",
	"audio/x-gsm",
	"audio/x-mace",
	"audio/x-monkeys-audio",
	"audio/x-mpeg",
452 453
	"audio/x-ms-wma",
	"audio/x-ms-wax",
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
	"audio/x-musepack",
	"audio/x-pn-realaudio",
	"audio/x-pn-multirate-realaudio",
	"audio/x-speex",
	"audio/x-tta"
	"audio/x-wav",
	"audio/x-wma",
	"audio/x-wv",
	"video/anim",
	"video/quicktime",
	"video/msvideo",
	"video/ogg",
	"video/theora",
	"video/x-dv",
	"video/x-flv",
	"video/x-matroska",
	"video/x-mjpeg",
	"video/x-mpeg",
	"video/x-ms-asf",
	"video/x-msvideo",
474 475 476 477
	"video/x-ms-wmv",
	"video/x-ms-wvx",
	"video/x-ms-wm",
	"video/x-ms-wmx",
478 479 480 481 482 483
	"video/x-nut",
	"video/x-pva",
	"video/x-theora",
	"video/x-vid",
	"video/x-wmv",
	"video/x-xvid",
484 485 486
	NULL
};

Max Kellermann's avatar
Max Kellermann committed
487
const struct decoder_plugin ffmpeg_plugin = {
488 489 490 491
	.name = "ffmpeg",
	.init = ffmpeg_init,
	.stream_decode = ffmpeg_decode,
	.tag_dup = ffmpeg_tag,
Max Kellermann's avatar
Max Kellermann committed
492 493
	.suffixes = ffmpeg_suffixes,
	.mime_types = ffmpeg_mime_types
494
};