From f6a705c7691a4b3b76c2d8e4abd1dbe65b8aa55d Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Wed, 13 Mar 2019 10:01:17 +0100 Subject: [PATCH] input/ffmpeg: add AVIOContext wrapper class --- src/input/plugins/FfmpegInputPlugin.cxx | 44 ++++-------- src/lib/ffmpeg/IOContext.hxx | 93 +++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 31 deletions(-) create mode 100644 src/lib/ffmpeg/IOContext.hxx diff --git a/src/input/plugins/FfmpegInputPlugin.cxx b/src/input/plugins/FfmpegInputPlugin.cxx index 7bdbb211a..3de948759 100644 --- a/src/input/plugins/FfmpegInputPlugin.cxx +++ b/src/input/plugins/FfmpegInputPlugin.cxx @@ -21,28 +21,25 @@ #define __STDC_CONSTANT_MACROS #include "FfmpegInputPlugin.hxx" +#include "lib/ffmpeg/IOContext.hxx" #include "lib/ffmpeg/Init.hxx" #include "lib/ffmpeg/Error.hxx" #include "../InputStream.hxx" #include "../InputPlugin.hxx" #include "PluginUnavailable.hxx" -extern "C" { -#include -} - class FfmpegInputStream final : public InputStream { - AVIOContext *h; + Ffmpeg::IOContext io; bool eof = false; public: - FfmpegInputStream(const char *_uri, Mutex &_mutex, - AVIOContext *_h) + FfmpegInputStream(const char *_uri, Mutex &_mutex) :InputStream(_uri, _mutex), - h(_h) { - seekable = (h->seekable & AVIO_SEEKABLE_NORMAL) != 0; - size = avio_size(h); + io(_uri, AVIO_FLAG_READ) + { + seekable = (io->seekable & AVIO_SEEKABLE_NORMAL) != 0; + size = io.GetSize(); /* hack to make MPD select the "ffmpeg" decoder plugin - since avio.h doesn't tell us the MIME type of the @@ -52,10 +49,6 @@ public: SetReady(); } - ~FfmpegInputStream() noexcept { - avio_close(h); - } - /* virtual methods from InputStream */ bool IsEOF() noexcept override; size_t Read(void *ptr, size_t size) override; @@ -84,28 +77,20 @@ static InputStreamPtr input_ffmpeg_open(const char *uri, Mutex &mutex) { - AVIOContext *h; - auto result = avio_open(&h, uri, AVIO_FLAG_READ); - if (result != 0) - throw MakeFfmpegError(result); - - return std::make_unique(uri, mutex, h); + return std::make_unique(uri, mutex); } size_t FfmpegInputStream::Read(void *ptr, size_t read_size) { - int result; + size_t result; { const ScopeUnlock unlock(mutex); - result = avio_read(h, (unsigned char *)ptr, read_size); + result = io.Read(ptr, read_size); } - if (result <= 0) { - if (result < 0) - throw MakeFfmpegError(result, "avio_read() failed"); - + if (result == 0) { eof = true; return 0; } @@ -123,16 +108,13 @@ FfmpegInputStream::IsEOF() noexcept void FfmpegInputStream::Seek(offset_type new_offset) { - int64_t result; + uint64_t result; { const ScopeUnlock unlock(mutex); - result = avio_seek(h, new_offset, SEEK_SET); + result = io.Seek(new_offset); } - if (result < 0) - throw MakeFfmpegError(result, "avio_seek() failed"); - offset = result; eof = false; } diff --git a/src/lib/ffmpeg/IOContext.hxx b/src/lib/ffmpeg/IOContext.hxx new file mode 100644 index 000000000..95ce637ee --- /dev/null +++ b/src/lib/ffmpeg/IOContext.hxx @@ -0,0 +1,93 @@ +/* + * Copyright 2003-2019 The Music Player Daemon Project + * 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. + */ + +#ifndef MPD_FFMPEG_IO_CONTEXT_HXX +#define MPD_FFMPEG_IO_CONTEXT_HXX + +#include "util/Compiler.h" +#include "Error.hxx" + +extern "C" { +#include +} + +#include + +namespace Ffmpeg { + +class IOContext { + AVIOContext *io_context = nullptr; + +public: + IOContext() = default; + + IOContext(const char *url, int flags) { + int err = avio_open(&io_context, url, flags); + if (err < 0) + throw MakeFfmpegError(err); + } + + ~IOContext() noexcept { + if (io_context != nullptr) + avio_close(io_context); + } + + IOContext(IOContext &&src) noexcept + :io_context(std::exchange(src.io_context, nullptr)) {} + + IOContext &operator=(IOContext &&src) noexcept { + using std::swap; + swap(io_context, src.io_context); + return *this; + } + + AVIOContext &operator*() noexcept { + return *io_context; + } + + AVIOContext *operator->() noexcept { + return io_context; + } + + gcc_pure + auto GetSize() const noexcept { + return avio_size(io_context); + } + + size_t Read(void *buffer, size_t size) { + int result = avio_read(io_context, + (unsigned char *)buffer, size); + if (result < 0) + throw MakeFfmpegError(result, "avio_read() failed"); + + return result; + } + + uint64_t Seek(uint64_t offset) { + int64_t result = avio_seek(io_context, offset, SEEK_SET); + if (result < 0) + throw MakeFfmpegError(result, "avio_seek() failed"); + + return result; + } +}; + +} // namespace Ffmpeg + +#endif