FfmpegInputPlugin.cxx 3.56 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright 2003-2021 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 23
/* necessary because libavutil/common.h uses UINT64_C */
#define __STDC_CONSTANT_MACROS

#include "FfmpegInputPlugin.hxx"
24
#include "lib/ffmpeg/IOContext.hxx"
25
#include "lib/ffmpeg/Init.hxx"
Max Kellermann's avatar
Max Kellermann committed
26
#include "../InputStream.hxx"
27
#include "PluginUnavailable.hxx"
28
#include "../InputPlugin.hxx"
29
#include "util/StringAPI.hxx"
30

31
class FfmpegInputStream final : public InputStream {
32
	Ffmpeg::IOContext io;
33

34
public:
35
	FfmpegInputStream(const char *_uri, Mutex &_mutex)
36
		:InputStream(_uri, _mutex),
37 38 39 40
		 io(_uri, AVIO_FLAG_READ)
	{
		seekable = (io->seekable & AVIO_SEEKABLE_NORMAL) != 0;
		size = io.GetSize();
41 42 43 44 45

		/* 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 */
46 47
		SetMimeType("audio/x-mpd-ffmpeg");
		SetReady();
48 49
	}

50
	/* virtual methods from InputStream */
51
	[[nodiscard]] bool IsEOF() const noexcept override;
52 53 54 55
	size_t Read(std::unique_lock<Mutex> &lock,
		    void *ptr, size_t size) override;
	void Seek(std::unique_lock<Mutex> &lock,
		  offset_type offset) override;
56 57
};

58
[[gnu::const]]
59
static inline bool
60
input_ffmpeg_supported() noexcept
61
{
62 63
	void *opaque = nullptr;
	return avio_enum_protocols(&opaque, 0) != nullptr;
64 65
}

66
static void
67
input_ffmpeg_init(EventLoop &, const ConfigBlock &)
68
{
69
	FfmpegInit();
70 71

	/* disable this plugin if there's no registered protocol */
72 73
	if (!input_ffmpeg_supported())
		throw PluginUnavailable("No protocol");
74 75
}

76
static std::set<std::string>
77 78
input_ffmpeg_protocols() noexcept
{
79 80 81 82
	void *opaque = nullptr;
	const char* protocol;
	std::set<std::string> protocols;
	while ((protocol = avio_enum_protocols(&opaque, 0))) {
83 84 85 86 87 88 89 90
		if (StringIsEqual(protocol, "hls")) {
			/* just "hls://" doesn't work, but these do
			   work: */
			protocols.emplace("hls+http://");
			protocols.emplace("hls+https://");
			continue;
		}

91 92 93 94 95 96
		if (protocol_is_whitelisted(protocol)) {
			std::string schema(protocol);
			schema.append("://");
			protocols.emplace(schema);
		}
	}
97

98 99 100
	return protocols;
}

101
static InputStreamPtr
102
input_ffmpeg_open(const char *uri,
103
		  Mutex &mutex)
104
{
105
	return std::make_unique<FfmpegInputStream>(uri, mutex);
106 107
}

108
size_t
109 110
FfmpegInputStream::Read(std::unique_lock<Mutex> &,
			void *ptr, size_t read_size)
111
{
112
	size_t result;
113 114 115

	{
		const ScopeUnlock unlock(mutex);
116
		result = io.Read(ptr, read_size);
117 118
	}

119 120
	offset += result;
	return (size_t)result;
121 122
}

123
bool
124
FfmpegInputStream::IsEOF() const noexcept
125
{
126
	return io.IsEOF();
127 128
}

129
void
130
FfmpegInputStream::Seek(std::unique_lock<Mutex> &, offset_type new_offset)
131
{
132
	uint64_t result;
133 134 135

	{
		const ScopeUnlock unlock(mutex);
136
		result = io.Seek(new_offset);
137
	}
138

139
	offset = result;
140 141
}

142
const InputPlugin input_plugin_ffmpeg = {
143
	"ffmpeg",
144
	nullptr,
145 146 147
	input_ffmpeg_init,
	nullptr,
	input_ffmpeg_open,
148
	input_ffmpeg_protocols
149
};