FfmpegInputPlugin.cxx 3.74 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2014 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

20 21 22
/* necessary because libavutil/common.h uses UINT64_C */
#define __STDC_CONSTANT_MACROS

23
#include "config.h"
24
#include "FfmpegInputPlugin.hxx"
25
#include "lib/ffmpeg/Init.hxx"
26
#include "lib/ffmpeg/Domain.hxx"
27
#include "lib/ffmpeg/Error.hxx"
Max Kellermann's avatar
Max Kellermann committed
28 29
#include "../InputStream.hxx"
#include "../InputPlugin.hxx"
30
#include "util/StringUtil.hxx"
31
#include "util/Error.hxx"
32

33
extern "C" {
34
#include <libavformat/avio.h>
35
}
36

37
struct FfmpegInputStream final : public InputStream {
38
	AVIOContext *h;
39 40

	bool eof;
41

42
	FfmpegInputStream(const char *_uri, Mutex &_mutex, Cond &_cond,
43
			  AVIOContext *_h)
44
		:InputStream(_uri, _mutex, _cond),
45
		 h(_h), eof(false) {
46 47
		seekable = (h->seekable & AVIO_SEEKABLE_NORMAL) != 0;
		size = avio_size(h);
48 49 50 51 52

		/* hack to make MPD select the "ffmpeg" decoder plugin
		   - since avio.h doesn't tell us the MIME type of the
		   resource, we can't select a decoder plugin, but the
		   "ffmpeg" plugin is quite good at auto-detection */
53 54
		SetMimeType("audio/x-mpd-ffmpeg");
		SetReady();
55 56 57 58 59
	}

	~FfmpegInputStream() {
		avio_close(h);
	}
60 61 62 63

	/* virtual methods from InputStream */
	bool IsEOF() override;
	size_t Read(void *ptr, size_t size, Error &error) override;
64
	bool Seek(offset_type offset, Error &error) override;
65 66
};

67 68 69
static inline bool
input_ffmpeg_supported(void)
{
70 71
	void *opaque = nullptr;
	return avio_enum_protocols(&opaque, 0) != nullptr;
72 73
}

74
static InputPlugin::InitResult
75
input_ffmpeg_init(gcc_unused const config_param &param,
76
		  Error &error)
77
{
78
	FfmpegInit();
79 80

	/* disable this plugin if there's no registered protocol */
81
	if (!input_ffmpeg_supported()) {
82
		error.Set(ffmpeg_domain, "No protocol");
83
		return InputPlugin::InitResult::UNAVAILABLE;
84 85
	}

86
	return InputPlugin::InitResult::SUCCESS;
87 88
}

89
static InputStream *
90
input_ffmpeg_open(const char *uri,
91
		  Mutex &mutex, Cond &cond,
92
		  Error &error)
93
{
94 95 96 97 98 99
	if (!StringStartsWith(uri, "gopher://") &&
	    !StringStartsWith(uri, "rtp://") &&
	    !StringStartsWith(uri, "rtsp://") &&
	    !StringStartsWith(uri, "rtmp://") &&
	    !StringStartsWith(uri, "rtmpt://") &&
	    !StringStartsWith(uri, "rtmps://"))
100
		return nullptr;
101

102
	AVIOContext *h;
103 104
	auto result = avio_open(&h, uri, AVIO_FLAG_READ);
	if (result != 0) {
105
		SetFfmpegError(error, result);
106
		return nullptr;
107 108
	}

109
	return new FfmpegInputStream(uri, mutex, cond, h);
110 111
}

112 113
size_t
FfmpegInputStream::Read(void *ptr, size_t read_size, Error &error)
114
{
115 116 117
	auto result = avio_read(h, (unsigned char *)ptr, read_size);
	if (result <= 0) {
		if (result < 0)
118
			SetFfmpegError(error, result, "avio_read() failed");
119

120
		eof = true;
121 122 123
		return false;
	}

124 125
	offset += result;
	return (size_t)result;
126 127
}

128 129
bool
FfmpegInputStream::IsEOF()
130
{
131
	return eof;
132 133
}

134
bool
135
FfmpegInputStream::Seek(offset_type new_offset, Error &error)
136
{
137
	auto result = avio_seek(h, new_offset, SEEK_SET);
138

139
	if (result < 0) {
140
		SetFfmpegError(error, result, "avio_seek() failed");
141 142
		return false;
	}
143

144
	offset = result;
145 146
	eof = false;
	return true;
147 148
}

149
const InputPlugin input_plugin_ffmpeg = {
150 151 152 153
	"ffmpeg",
	input_ffmpeg_init,
	nullptr,
	input_ffmpeg_open,
154
};