diff --git a/CMakeLists.txt b/CMakeLists.txt index dd2c4e6e8..d5112218d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,7 @@ option(WITH_DEVILIMAGEIMPORTER "Build DevIlImageImporter plugin" OFF) option(WITH_DRFLACAUDIOIMPORTER "Build DrFlacAudioImporter plugin" OFF) option(WITH_DRWAVAUDIOIMPORTER "Build DrWavAudioImporter plugin" OFF) option(WITH_FAAD2AUDIOIMPORTER "Build Faad2AudioImporter plugin" OFF) +option(WITH_FFMPEGAUDIOIMPORTER "Build FfmpegAudioImporter plugin" OFF) cmake_dependent_option(WITH_FREETYPEFONT "Build FreeTypeFont plugin" OFF "NOT WITH_HARFBUZZFONT" ON) option(WITH_HARFBUZZFONT "Build HarfBuzzFont plugin" OFF) option(WITH_JPEGIMAGECONVERTER "Build JpegImageConverter plugin" OFF) diff --git a/doc/building-plugins.dox b/doc/building-plugins.dox index b6ea123ae..26d6dda48 100644 --- a/doc/building-plugins.dox +++ b/doc/building-plugins.dox @@ -279,6 +279,9 @@ By default no plugins are built and you need to select them manually: - `WITH_FAAD2AUDIOIMPORTER` --- Build the @ref Audio::Faad2Importer "Faad2AudioImporter" plugin. Depends on [FAAD2](https://www.audiocoding.com/faad2.html). +- `WITH_FFMPEGAUDIOIMPORTER` --- Build the + @ref Audio::FfmpegImporter "FfmpegAudioImporter" plugin. Depends on + [FFmpeg](https://www.ffmpeg.org/). - `WITH_FREETYPEFONT` --- Build the @ref Text::FreeTypeFont "FreeTypeFont" plugin. Depends on [FreeType](http://www.freetype.org/). - `WITH_HARFBUZZFONT` --- Build the @ref Text::HarfBuzzFont "HarfBuzzFont" diff --git a/doc/changelog-plugins.dox b/doc/changelog-plugins.dox index ccc2f9bbf..2f4771481 100644 --- a/doc/changelog-plugins.dox +++ b/doc/changelog-plugins.dox @@ -46,6 +46,8 @@ namespace Magnum { - New @ref Audio::Faad2Importer "Faad2AudioImporter" plugin for importing AAC files using [FAAD2](https://www.audiocoding.com/faad2.html) +- New @ref Audio::FfmpegImporter "FfmpegAudioImporter" plugin for importing + virtually all existing audio formats @subsection changelog-plugins-latest-changes Changes and improvements diff --git a/doc/cmake-plugins.dox b/doc/cmake-plugins.dox index 59c7d5267..ad63fe14f 100644 --- a/doc/cmake-plugins.dox +++ b/doc/cmake-plugins.dox @@ -74,6 +74,8 @@ This command will not try to find any actual plugin. The plugins are: plugin - `Faad2AudioImporter` --- @ref Audio::Faad2Importer "Faad2AudioImporter" plugin +- `FfmpegAudioImporter` --- @ref Audio::FfmpegImporter "FfmpegAudioImporter" + plugin - `FreeTypeFont` --- @ref Text::FreeTypeFont "FreeTypeFont" plugin - `HarfBuzzFont` --- @ref Text::HarfBuzzFont "HarfBuzzFont" plugin - `JpegImageConverter` --- @ref Trade::JpegImageConverter "JpegImageConverter" diff --git a/modules/FindFFmpeg.cmake b/modules/FindFFmpeg.cmake new file mode 100644 index 000000000..9bfba9d70 --- /dev/null +++ b/modules/FindFFmpeg.cmake @@ -0,0 +1,86 @@ +#.rst: +# Find FFmpeg +# ------------- +# +# Finds the FFmpeg library. Basic usage: +# +# find_package(FFmpeg REQUIRED avformat avcodec) +# +# This module defines: +# +# FFmpeg_FOUND - True if FFmpeg is found +# FFmpeg__FOUND - True if given FFmpeg compoment is found +# FFmpeg::ffmpeg - Imported target for the ffmpeg executable +# FFmpeg:: - Imported target for given library component +# +# The is any of these: +# +# avcodec +# avdevice +# avfilter +# avformat +# avresample +# avutil +# swresample +# swscale +# +# Additionally these variables are defined for internal usage: +# +# FFmpeg__LIBRARY - FFmpeg component library location +# FFmpeg__INCLUDE_DIR - FFmpeg component Include dir +# + +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 +# Vladimír Vondruš +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# + +find_program(FFmpeg_EXECUTABLE ffmpeg) +if(FFmpeg_EXECUTABLE) + add_executable(FFmpeg::ffmpeg IMPORTED) + mark_as_advanced(FFmpeg_EXECUTABLE) + set_property(TARGET FFmpeg::ffmpeg PROPERTY IMPORTED_LOCATION ${FFmpeg_EXECUTABLE}) +endif() + +foreach(_component ${FFmpeg_FIND_COMPONENTS}) + find_library(FFmpeg_${_component}_LIBRARY NAMES ${_component}) + find_path(FFmpeg_${_component}_INCLUDE_DIR NAMES lib${_component}/${_component}.h) + + if(FFmpeg_${_component}_LIBRARY AND FFmpeg_${_component}_INCLUDE_DIR) + set(FFmpeg_${_component}_FOUND ON) + + if(NOT TARGET FFmpeg::${_component}) + add_library(FFmpeg::${_component} UNKNOWN IMPORTED) + set_target_properties(FFmpeg::${_component} PROPERTIES + IMPORTED_LOCATION ${FFmpeg_${_component}_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${FFmpeg_${_component}_INCLUDE_DIR}) + endif() + endif() + + mark_as_advanced( + FFmpeg_${_component}_LIBRARY + FFmpeg_${_component}_INCLUDE_DIR) +endforeach() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(FFmpeg REQUIRED_VARS FFmpeg_EXECUTABLE HANDLE_COMPONENTS) diff --git a/modules/FindMagnumPlugins.cmake b/modules/FindMagnumPlugins.cmake index c63253070..bfe5ab571 100644 --- a/modules/FindMagnumPlugins.cmake +++ b/modules/FindMagnumPlugins.cmake @@ -126,11 +126,11 @@ mark_as_advanced(MAGNUMPLUGINS_INCLUDE_DIR) set(_MAGNUMPLUGINS_LIBRARY_COMPONENT_LIST OpenDdl) set(_MAGNUMPLUGINS_PLUGIN_COMPONENT_LIST AssimpImporter DdsImporter DevIlImageImporter - DrFlacAudioImporter DrWavAudioImporter Faad2AudioImporter FreeTypeFont - HarfBuzzFont JpegImageConverter JpegImporter MiniExrImageConverter - OpenGexImporter PngImageConverter PngImporter StanfordImporter - StbImageConverter StbImageImporter StbTrueTypeFont StbVorbisAudioImporter - TinyGltfImporter) + DrFlacAudioImporter DrWavAudioImporter Faad2AudioImporter + FfmpegAudioImporter FreeTypeFont HarfBuzzFont JpegImageConverter + JpegImporter MiniExrImageConverter OpenGexImporter PngImageConverter + PngImporter StanfordImporter StbImageConverter StbImageImporter + StbTrueTypeFont StbVorbisAudioImporter TinyGltfImporter) if(MAGNUM_BUILD_DEPRECATED) list(APPEND _MAGNUMPLUGINS_PLUGIN_COMPONENT_LIST ColladaImporter) endif() @@ -297,6 +297,12 @@ foreach(_component ${MagnumPlugins_FIND_COMPONENTS}) set_property(TARGET MagnumPlugins::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES FAAD2::FAAD2) + # FfmpegAudioImporter plugin dependencies + elseif(_component STREQUAL FffmpegAudioImporter) + find_package(FFmpeg REQUIRED avcodec avformat avutil) + set_property(TARGET MagnumPlugins::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES FFmpeg::avcodec FFmpeg::avformat FFmpeg::avutil) + # FreeTypeFont plugin dependencies elseif(_component STREQUAL FreeTypeFont) find_package(Freetype) diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD index 5eea376d1..a26156c43 100644 --- a/package/archlinux/PKGBUILD +++ b/package/archlinux/PKGBUILD @@ -6,7 +6,7 @@ pkgdesc="Plugins for the Magnum C++11/C++14 graphics engine" arch=('i686' 'x86_64') url="https://magnum.graphics" license=('MIT') -depends=('magnum' 'qt4' 'devil' 'faad2' 'freetype2' 'harfbuzz' 'libjpeg' 'libpng' 'assimp') +depends=('magnum' 'qt4' 'devil' 'faad2' 'ffmpeg' 'freetype2' 'harfbuzz' 'libjpeg' 'libpng' 'assimp') makedepends=('cmake' 'ninja') options=(!strip) provides=('magnum-plugins-git') @@ -29,6 +29,7 @@ build() { -DWITH_DRFLACAUDIOIMPORTER=ON \ -DWITH_DRWAVAUDIOIMPORTER=ON \ -DWITH_FAAD2AUDIOIMPORTER=ON \ + -DWITH_FFMPEGAUDIOIMPORTER=ON \ -DWITH_FREETYPEFONT=ON \ -DWITH_HARFBUZZFONT=ON \ -DWITH_JPEGIMAGECONVERTER=ON \ diff --git a/src/MagnumPlugins/CMakeLists.txt b/src/MagnumPlugins/CMakeLists.txt index e76e98ec3..20a445fad 100644 --- a/src/MagnumPlugins/CMakeLists.txt +++ b/src/MagnumPlugins/CMakeLists.txt @@ -62,6 +62,10 @@ if(WITH_FAAD2AUDIOIMPORTER) add_subdirectory(Faad2AudioImporter) endif() +if(WITH_FFMPEGAUDIOIMPORTER) + add_subdirectory(FfmpegAudioImporter) +endif() + if(WITH_FREETYPEFONT) add_subdirectory(FreeTypeFont) endif() diff --git a/src/MagnumPlugins/FfmpegAudioImporter/CMakeLists.txt b/src/MagnumPlugins/FfmpegAudioImporter/CMakeLists.txt new file mode 100644 index 000000000..4ec7d6f54 --- /dev/null +++ b/src/MagnumPlugins/FfmpegAudioImporter/CMakeLists.txt @@ -0,0 +1,70 @@ +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 +# Vladimír Vondruš +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# + +find_package(Magnum REQUIRED Audio) +find_package(FFmpeg REQUIRED avcodec avformat avutil) + +if(BUILD_PLUGINS_STATIC) + set(MAGNUM_FFMPEGAUDIOIMPORTER_BUILD_STATIC 1) +endif() + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/configure.h) + +# FfmpegAudioImporter plugin +add_plugin(FfmpegAudioImporter + "${MAGNUM_PLUGINS_AUDIOIMPORTER_DEBUG_BINARY_INSTALL_DIR};${MAGNUM_PLUGINS_AUDIOIMPORTER_DEBUG_LIBRARY_INSTALL_DIR}" + "${MAGNUM_PLUGINS_AUDIOIMPORTER_RELEASE_BINARY_INSTALL_DIR};${MAGNUM_PLUGINS_AUDIOIMPORTER_RELEASE_LIBRARY_INSTALL_DIR}" + FfmpegAudioImporter.conf + FfmpegImporter.cpp + FfmpegImporter.h) +if(BUILD_PLUGINS_STATIC AND BUILD_STATIC_PIC) + set_target_properties(FfmpegAudioImporter PROPERTIES POSITION_INDEPENDENT_CODE ON) +endif() +target_include_directories(FfmpegAudioImporter PUBLIC + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_BINARY_DIR}/src) +# Include the stb_*.h files as a system directory to supress warnings +target_include_directories(FfmpegAudioImporter SYSTEM PRIVATE ${PROJECT_SOURCE_DIR}/src/external/stb) +target_link_libraries(FfmpegAudioImporter PUBLIC + Magnum::Audio FFmpeg::avcodec FFmpeg::avformat FFmpeg::avutil) + +install(FILES FfmpegImporter.h ${CMAKE_CURRENT_BINARY_DIR}/configure.h + DESTINATION ${MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR}/FfmpegAudioImporter) + +# Automatic static plugin import +if(BUILD_PLUGINS_STATIC) + install(FILES importStaticPlugin.cpp DESTINATION ${MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR}/FfmpegAudioImporter) + if(NOT CMAKE_VERSION VERSION_LESS 3.1) + target_sources(FfmpegAudioImporter INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/importStaticPlugin.cpp) + endif() +endif() + +if(BUILD_TESTS) + add_subdirectory(Test) +endif() + +# MagnumPlugins FfmpegAudioImporter target alias for superprojects +add_library(MagnumPlugins::FfmpegAudioImporter ALIAS FfmpegAudioImporter) diff --git a/src/MagnumPlugins/FfmpegAudioImporter/FfmpegAudioImporter.conf b/src/MagnumPlugins/FfmpegAudioImporter/FfmpegAudioImporter.conf new file mode 100644 index 000000000..e69de29bb diff --git a/src/MagnumPlugins/FfmpegAudioImporter/FfmpegImporter.cpp b/src/MagnumPlugins/FfmpegAudioImporter/FfmpegImporter.cpp new file mode 100644 index 000000000..2bef77c3e --- /dev/null +++ b/src/MagnumPlugins/FfmpegAudioImporter/FfmpegImporter.cpp @@ -0,0 +1,453 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "FfmpegImporter.h" + +#include +#include +#include +#include +#include + + +extern "C" { +#include +#include +#include +} + +#if __clang__ +#include +void printAudioFrameInfo(const AVCodecContext* codecContext, const AVFrame* frame) +{ + // See the following to know what data type (unsigned char, short, float, etc) to use to access the audio data: + // http://ffmpeg.org/doxygen/trunk/samplefmt_8h.html#af9a51ca15301871723577c730b5865c5 + std::cout << "Audio frame info:\n" + << " Sample count: " << frame->nb_samples << '\n' + << " Channel count: " << codecContext->channels << '\n' + << " Format: " << av_get_sample_fmt_name(codecContext->sample_fmt) << '\n' + << " Bytes per sample: " << av_get_bytes_per_sample(codecContext->sample_fmt) << '\n' + << " Is planar? " << av_sample_fmt_is_planar(codecContext->sample_fmt) << '\n'; + + + std::cout << "frame->linesize[0] tells you the size (in bytes) of each plane\n"; + + if (codecContext->channels > AV_NUM_DATA_POINTERS && av_sample_fmt_is_planar(codecContext->sample_fmt)) + { + std::cout << "The audio stream (and its frames) have too many channels to fit in\n" + << "frame->data. Therefore, to access the audio data, you need to use\n" + << "frame->extended_data to access the audio data. It's planar, so\n" + << "each channel is in a different element. That is:\n" + << " frame->extended_data[0] has the data for channel 1\n" + << " frame->extended_data[1] has the data for channel 2\n" + << " etc.\n"; + } + else + { + std::cout << "Either the audio data is not planar, or there is enough room in\n" + << "frame->data to store all the channels, so you can either use\n" + << "frame->data or frame->extended_data to access the audio data (they\n" + << "should just point to the same data).\n"; + } + + std::cout << "If the frame is planar, each channel is in a different element.\n" + << "That is:\n" + << " frame->data[0]/frame->extended_data[0] has the data for channel 1\n" + << " frame->data[1]/frame->extended_data[1] has the data for channel 2\n" + << " etc.\n"; + + std::cout << "If the frame is packed (not planar), then all the data is in\n" + << "frame->data[0]/frame->extended_data[0] (kind of like how some\n" + << "image formats have RGB pixels packed together, rather than storing\n" + << " the red, green, and blue channels separately in different arrays.\n"; +} + +// #if 0 +int main() +{ + // Initialize FFmpeg + av_register_all(); // TODO: not needed at all in v4(?) + + AVFrame* frame = avcodec_alloc_frame(); + if (!frame) + { + std::cout << "Error allocating the frame" << std::endl; + return 1; + } + + // you can change the file name "01 Push Me to the Floor.wav" to whatever the file is you're reading, like "myFile.ogg" or + // "someFile.webm" and this should still work + AVFormatContext* formatContext = NULL; + if (avformat_open_input(&formatContext, "../CarBots Marines vs. Zerglings-WKvX3a2J86s.mp4", NULL, NULL) != 0) + { + av_free(frame); + std::cout << "Error opening the file" << std::endl; + return 1; + } + + if (avformat_find_stream_info(formatContext, NULL) < 0) + { + av_free(frame); + avformat_close_input(&formatContext); + std::cout << "Error finding the stream info" << std::endl; + return 1; + } + + // Find the audio stream + AVCodec* cdc = nullptr; + int streamIndex = av_find_best_stream(formatContext, AVMEDIA_TYPE_AUDIO, -1, -1, &cdc, 0); + if (streamIndex < 0) + { + av_free(frame); + avformat_close_input(&formatContext); + std::cout << "Could not find any audio stream in the file" << std::endl; + return 1; + } + + AVStream* audioStream = formatContext->streams[streamIndex]; + auto a = audioStream->codecpar->codec_id; + AVCodecContext* codecContext = audioStream->codec; // TODO: should use codecpar (since ?!) but *how* + codecContext->codec = cdc; + + if (avcodec_open2(codecContext, codecContext->codec, NULL) != 0) + { + av_free(frame); + avformat_close_input(&formatContext); + std::cout << "Couldn't open the context with the decoder" << std::endl; + return 1; + } + + std::cout << "This stream has " << codecContext->channels << " channels and a sample rate of " << codecContext->sample_rate << "Hz" << std::endl; + std::cout << "The data is in the format " << av_get_sample_fmt_name(codecContext->sample_fmt) << std::endl; + + AVPacket readingPacket; + av_init_packet(&readingPacket); + + // Read the packets in a loop + while (av_read_frame(formatContext, &readingPacket) == 0) + { + if (readingPacket.stream_index == audioStream->index) + { + AVPacket decodingPacket = readingPacket; + + // Audio packets can have multiple audio frames in a single packet + while (decodingPacket.size > 0) + { + // Try to decode the packet into a frame + // Some frames rely on multiple packets, so we have to make sure the frame is finished before + // we can use it + int gotFrame = 0; + int result = avcodec_decode_audio4(codecContext, frame, &gotFrame, &decodingPacket); // TODO: deprecated since 4.0, see https://ffmpeg.org/doxygen/4.1/group__lavc__decoding.html#gaaa1fbe477c04455cdc7a994090100db4 + + if (result >= 0 && gotFrame) + { + decodingPacket.size -= result; + decodingPacket.data += result; + + // We now have a fully decoded audio frame + printAudioFrameInfo(codecContext, frame); + } + else + { + decodingPacket.size = 0; + decodingPacket.data = nullptr; + } + } + } + + // You *must* call av_free_packet() after each call to av_read_frame() or else you'll leak memory + av_free_packet(&readingPacket); // TODO: av_packet_unref since 3.1(?) + } + + // Some codecs will cause frames to be buffered up in the decoding process. If the CODEC_CAP_DELAY flag + // is set, there can be buffered up frames that need to be flushed, so we'll do that + if (codecContext->codec->capabilities & CODEC_CAP_DELAY) + { + av_init_packet(&readingPacket); + // Decode all the remaining frames in the buffer, until the end is reached + int gotFrame = 0; + while (avcodec_decode_audio4(codecContext, frame, &gotFrame, &readingPacket) >= 0 && gotFrame) + { + // We now have a fully decoded audio frame + printAudioFrameInfo(codecContext, frame); + } + } + + // Clean up! + av_free(frame); + avcodec_close(codecContext); + avformat_close_input(&formatContext); +} +#endif + +namespace Magnum { namespace Audio { + +FfmpegImporter::FfmpegImporter() = default; + +FfmpegImporter::FfmpegImporter(PluginManager::AbstractManager& manager, const std::string& plugin): AbstractImporter{manager, plugin} {} + +auto FfmpegImporter::doFeatures() const -> Features { return Feature::OpenData; } + +bool FfmpegImporter::doIsOpened() const { return _data; } + +void FfmpegImporter::doOpenData(Containers::ArrayView data) { + /* https://stackoverflow.com/a/20610535 */ + /* http://www.ffmpeg.org/doxygen/trunk/doc_2examples_2avio_reading_8c-example.html */ + + /* Fumble the bumbles */ + /* Why the HELL do I need to allocate to read a file from memory?! WHY THE + DAMN HELL IS THIS SO FUCKING OVERENGINEERED?! FUCK!!! */ + constexpr std::size_t AvioBufferSize = 4096; + std::uint8_t* avioBuffer = reinterpret_cast(av_malloc(AvioBufferSize)); + struct AvioData { + Containers::ArrayView view; + std::size_t seek; + } avioData{data, 0}; + auto read = [](void* userPtr, std::uint8_t* buffer, int size) { + auto& avioData = *static_cast(userPtr); + const std::size_t end = Math::min(avioData.seek + size, avioData.view.size()); + int count = end - avioData.seek; + std::copy(avioData.view + avioData.seek, avioData.view + end, buffer); + avioData.seek = end; + + /* End-of-file has to be returned as a special value instead of just + "zero bytes read", otherwise it causes + "Invalid return value 0 for stream protocol" to be printed */ + return count ? count : AVERROR_EOF; + }; + AVIOContext* const io = avio_alloc_context(avioBuffer, AvioBufferSize, 0, &avioData, read, nullptr, nullptr); + CORRADE_INTERNAL_ASSERT(io); + Containers::ScopedExit ioBufferFree{&io->buffer, av_freep}; + + /* Fringle the pringles */ + AVFormatContext* formatContext = avformat_alloc_context(); + CORRADE_INTERNAL_ASSERT(formatContext); + Containers::ScopedExit formatContextClose{&formatContext, avformat_close_input}; + formatContext->pb = io; + if(const int ret = avformat_open_input(&formatContext, nullptr, nullptr, nullptr)) { + Error{} << "Audio::FfmpegImporter::openData(): could not open the file:" << /*av_err2str*/(ret); + return; + } + + /* Rustle the rumbles */ + /** @todo why the fuck am i doing this why the damn hell, it doesn't return any fucking thing. ALSO HOW CAN THIS FAIL?! */ + if(const int ret = avformat_find_stream_info(formatContext, nullptr)) { + Error{} << "Audio::FfmpegImporter::openData(): could not find stream info:" << /*av_err2str*/(ret); + return; + } + + /* Fizzle the bizzle */ + AVCodec* codec; + const int streamIndex = av_find_best_stream(formatContext, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0); + if(streamIndex < 0) { + Error{} << "Audio::FfmpegImporter::openData(): could not find any audio stream in the file"; + return; + } + + /* Light my fire */ + AVStream* audioStream = formatContext->streams[streamIndex]; + /** @todo should be codecpar (since when?!) but how?! */ + AVCodecContext* codecContext = audioStream->codec; + codecContext->codec = codec; + if(const int ret = avcodec_open2(codecContext, codecContext->codec, nullptr) != 0) { + Error{} << "Audio::FfmpegImporter::openData(): could not open context with chosen decoder"; + return; + } + + /* FLIGHT MY RIFLE */ + _frequency = codecContext->sample_rate; + _format = {}; + const Int channelCount = codecContext->channels; +// const Int bytesPerSample = codecContext->sample_fmt; + if(codecContext->sample_fmt == AV_SAMPLE_FMT_U8 || + codecContext->sample_fmt == AV_SAMPLE_FMT_U8P) { + if(channelCount == 1) + _format = BufferFormat::Mono8; + else if(channelCount == 2) + _format = BufferFormat::Stereo8; + else if(channelCount == 4) + _format = BufferFormat::Quad8; + else if(channelCount == 6) + _format = BufferFormat::Surround51Channel8; + else if(channelCount == 7) + _format = BufferFormat::Surround61Channel8; + else if(channelCount == 8) + _format = BufferFormat::Surround71Channel8; + } else if(codecContext->sample_fmt == AV_SAMPLE_FMT_S16 || + codecContext->sample_fmt == AV_SAMPLE_FMT_S16P) { + if(channelCount == 1) + _format = BufferFormat::Mono16; + else if(channelCount == 2) + _format = BufferFormat::Stereo16; + else if(channelCount == 4) + _format = BufferFormat::Quad16; + else if(channelCount == 6) + _format = BufferFormat::Surround51Channel16; + else if(channelCount == 7) + _format = BufferFormat::Surround61Channel16; + else if(channelCount == 8) + _format = BufferFormat::Surround71Channel16; + } else if(codecContext->sample_fmt == AV_SAMPLE_FMT_FLT || + codecContext->sample_fmt == AV_SAMPLE_FMT_FLTP) { + if(channelCount == 1) + _format = BufferFormat::MonoFloat; + else if(channelCount == 2) + _format = BufferFormat::StereoFloat; + } else if(codecContext->sample_fmt == AV_SAMPLE_FMT_DBL || + codecContext->sample_fmt == AV_SAMPLE_FMT_DBLP) { + if(channelCount == 1) + _format = BufferFormat::MonoDouble; + else if(channelCount == 2) + _format = BufferFormat::StereoDouble; + } + if(!ALenum(_format)) { + Error() << "Audio::FfmpegImporter::openData(): unsupported format" + << av_get_sample_fmt_name(codecContext->sample_fmt) << "with" + << channelCount << "channels"; + return; + } + + Debug{} << channelCount << _frequency << av_get_sample_fmt_name(codecContext->sample_fmt); + + /* Foom the boomble */ + AVPacket readingPacket; + av_init_packet(&readingPacket); + AVFrame* frame = av_frame_alloc(); // was avcodec_alloc_frame() before + CORRADE_INTERNAL_ASSERT(frame); + const std::size_t sampleSize = av_get_bytes_per_sample(codecContext->sample_fmt); + std::vector decoded; + /* Otherwise we would need to look into extra_data. AV_NUM_DATA_POINTERS is + 8 on my machine and max supported config is 7.1, so it should fit. */ + CORRADE_INTERNAL_ASSERT(codecContext->channels <= AV_NUM_DATA_POINTERS); + while(av_read_frame(formatContext, &readingPacket) == 0) { + if(readingPacket.stream_index == audioStream->index) { // TODO: Y not streamIndex? + AVPacket decodingPacket = readingPacket; + + /* Audio packets can have multiple audio frames in a single packet, + OTOH one frame can be also split across multiple frames */ + while(decodingPacket.size > 0) { + int frameDone = 0; + const int result = avcodec_decode_audio4(codecContext, frame, &frameDone, &decodingPacket); // TODO: deprecated since 4.0, see https://ffmpeg.org/doxygen/4.1/group__lavc__decoding.html#gaaa1fbe477c04455cdc7a994090100db4 + + if(result >= 0 && frameDone) { + decodingPacket.size -= result; + decodingPacket.data += result; + + // We now have a fully decoded audio frame + + const std::size_t pos = decoded.size(); + decoded.resize(decoded.size() + frame->nb_samples*sampleSize); +// printAudioFrameInfo(codecContext, frame); + + /* Data for each channel are at a separate location, + interleave them back */ + if(av_sample_fmt_is_planar(codecContext->sample_fmt) && channelCount > 1) { + for(std::size_t sample = 0; sample != std::size_t(frame->nb_samples); ++sample) { + for(std::size_t channel = 0; channel != std::size_t(channelCount); ++channel) { + for(std::size_t i = 0; i != sampleSize; ++i) + decoded[pos + sample*sampleSize + i] = frame->data[channel][sample*sampleSize + i]; + } + } + +// av_get_bytes_per_sample(codecContext->sample_fmt) + + /* Otherwise just memcpy the whole thing */ + } else std::copy_n(frame->data[0], frame->nb_samples*sampleSize, &decoded[pos]); + + } else { + decodingPacket.size = 0; + decodingPacket.data = nullptr; + } + } + } + + // You *must* call av_free_packet() after each call to av_read_frame() or else you'll leak memory + av_free_packet(&readingPacket); // TODO: av_packet_unref since 3.1(?) + } + + /* All good, copy the data to an array */ + _data = Containers::Array{decoded.size()}; + std::copy(decoded.begin(), decoded.end(), _data.begin()); + +#if 0 + Int numChannels, frequency; + Short* decodedData = nullptr; + + Int samples = stb_vorbis_decode_memory(reinterpret_cast(data.data()), data.size(), &numChannels, &frequency, &decodedData); + + if(samples == -1) { + Error() << "Audio::FfmpegImporter::openData(): the file signature is invalid"; + return; + } else if (samples == -2) { + /* memory allocation failure */ + Error() << "Audio::FfmpegImporter::openData(): out of memory"; + return; + } + + Containers::Array tempData{reinterpret_cast(decodedData), size_t(samples*numChannels*2), + [](char* data, size_t) { std::free(data); }}; + _frequency = frequency; + + /** @todo Floating-point formats */ + if(numChannels == 1) + _format = BufferFormat::Mono16; + else if(numChannels == 2) + _format = BufferFormat::Stereo16; + else if(numChannels == 4) + _format = BufferFormat::Quad16; + else if(numChannels == 6) + _format = BufferFormat::Surround51Channel16; + else if(numChannels == 7) + _format = BufferFormat::Surround61Channel16; + else if(numChannels == 8) + _format = BufferFormat::Surround71Channel16; + else { + Error() << "Audio::FfmpegImporter::openData(): unsupported channel count" + << numChannels << "with" << 16 << "bits per sample"; + return; + } + + _data = std::move(tempData); +#endif + + return; +} + +void FfmpegImporter::doClose() { _data = nullptr; } + +BufferFormat FfmpegImporter::doFormat() const { return _format; } + +UnsignedInt FfmpegImporter::doFrequency() const { return _frequency; } + +Containers::Array FfmpegImporter::doData() { + Containers::Array copy(_data.size()); + std::copy(_data.begin(), _data.end(), copy.begin()); + return copy; +} + +}} + +CORRADE_PLUGIN_REGISTER(StbVorbisAudioImporter, Magnum::Audio::FfmpegImporter, + "cz.mosra.magnum.Audio.AbstractImporter/0.1") diff --git a/src/MagnumPlugins/FfmpegAudioImporter/FfmpegImporter.h b/src/MagnumPlugins/FfmpegAudioImporter/FfmpegImporter.h new file mode 100644 index 000000000..e5eba595b --- /dev/null +++ b/src/MagnumPlugins/FfmpegAudioImporter/FfmpegImporter.h @@ -0,0 +1,107 @@ +#ifndef Magnum_Audio_FfmpegImporter_h +#define Magnum_Audio_FfmpegImporter_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Class @ref Magnum::Audio::FfmpegImporter + */ + +#include +#include + +#include "MagnumPlugins/FfmpegAudioImporter/configure.h" + +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_FFMPEGAUDIOIMPORTER_BUILD_STATIC + #ifdef FfmpegAudioImporter_EXPORTS + #define MAGNUM_FFMPEGAUDIOIMPORTER_EXPORT CORRADE_VISIBILITY_EXPORT + #else + #define MAGNUM_FFMPEGAUDIOIMPORTER_EXPORT CORRADE_VISIBILITY_IMPORT + #endif +#else + #define MAGNUM_FFMPEGAUDIOIMPORTER_EXPORT CORRADE_VISIBILITY_STATIC +#endif +#define MAGNUM_FFMPEGAUDIOIMPORTER_LOCAL CORRADE_VISIBILITY_LOCAL +#else +#define MAGNUM_FFMPEGAUDIOIMPORTER_EXPORT +#define MAGNUM_FFMPEGAUDIOIMPORTER_LOCAL +#endif + +namespace Magnum { namespace Audio { + +/** +@brief Audio importer using FFmpeg + +@todoc formats, link + +This plugin depends on the @ref Audio library and is built if +`WITH_FFMPEGAUDIOIMPORTER` is enabled when building Magnum Plugins. To use +as a dynamic plugin, you need to load the @cpp "FfmpegAudioImporter" @ce +plugin from `MAGNUM_PLUGINS_AUDIOIMPORTER_DIR`. To use as a static plugin or as +a dependency of another plugin with CMake, you need to request the +`FfmpegAudioImporter` component of the `MagnumPlugins` package and link to +the `MagnumPlugins::FfmpegAudioImporter` target. + +@todoc provides + +@m_class{m-block m-warning} + +@thirdparty This plugin makes use of the [FFmpeg](http://assimp.org) library, + licensed under @m_class{m-label m-warning} **LGPLv2.1** + ([license text](https://www.ffmpeg.org/legal.html), + [choosealicense.com](https://choosealicense.com/licenses/lgpl-2.1/)). It + requires attribution and either dynamic linking or source disclosure for + public use. + +See @ref building-plugins, @ref cmake-plugins and @ref plugins for more +information. +*/ +class MAGNUM_FFMPEGAUDIOIMPORTER_EXPORT FfmpegImporter: public AbstractImporter { + public: + /** @brief Default constructor */ + explicit FfmpegImporter(); + + /** @brief Plugin manager constructor */ + explicit FfmpegImporter(PluginManager::AbstractManager& manager, const std::string& plugin); + + private: + MAGNUM_FFMPEGAUDIOIMPORTER_LOCAL Features doFeatures() const override; + MAGNUM_FFMPEGAUDIOIMPORTER_LOCAL bool doIsOpened() const override; + MAGNUM_FFMPEGAUDIOIMPORTER_LOCAL void doOpenData(Containers::ArrayView data) override; + MAGNUM_FFMPEGAUDIOIMPORTER_LOCAL void doClose() override; + + MAGNUM_FFMPEGAUDIOIMPORTER_LOCAL BufferFormat doFormat() const override; + MAGNUM_FFMPEGAUDIOIMPORTER_LOCAL UnsignedInt doFrequency() const override; + MAGNUM_FFMPEGAUDIOIMPORTER_LOCAL Containers::Array doData() override; + + Containers::Array _data; + BufferFormat _format; + UnsignedInt _frequency; +}; + +}} + +#endif diff --git a/src/MagnumPlugins/FfmpegAudioImporter/Test/CMakeLists.txt b/src/MagnumPlugins/FfmpegAudioImporter/Test/CMakeLists.txt new file mode 100644 index 000000000..4b44f3d1b --- /dev/null +++ b/src/MagnumPlugins/FfmpegAudioImporter/Test/CMakeLists.txt @@ -0,0 +1,68 @@ +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 +# Vladimír Vondruš +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# + +if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) + set(MONO16_OGG_FILE mono16.ogg) + set(STEREO8_OGG_FILE stereo8.ogg) + set(UNSUPPORTED_CHANNEL_COUNT_OGG_FILE unsupportedChannelCount.ogg) + set(WRONG_SIGNATURE_OGG_FILE wrongSignature.ogg) +else() + set(MONO16_OGG_FILE ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/StbVorbisAudioImporter/Test/mono16.ogg) + set(STEREO8_OGG_FILE ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/StbVorbisAudioImporter/Test/stereo8.ogg) + set(UNSUPPORTED_CHANNEL_COUNT_OGG_FILE ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/StbVorbisAudioImporter/Test/unsupportedChannelCount.ogg) + set(WRONG_SIGNATURE_OGG_FILE ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/StbVorbisAudioImporter/Test/wrongSignature.ogg) +endif() + +# CMake before 3.8 has broken $ expressions for iOS (see +# https://gitlab.kitware.com/cmake/cmake/merge_requests/404) and since Corrade +# doesn't support dynamic plugins on iOS, this sorta works around that. Should +# be revisited when updating Travis to newer Xcode (current has CMake 3.6). +if(NOT BUILD_PLUGINS_STATIC) + set(FFMPEGAUDIOIMPORTER_PLUGIN_FILENAME $) + + # First replace ${} variables, then $<> generator expressions + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) + file(GENERATE OUTPUT $/configure.h + INPUT ${CMAKE_CURRENT_BINARY_DIR}/configure.h.in) +else() + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/configure.h) +endif() + +corrade_add_test(FfmpegAudioImporterTest FfmpegImporterTest.cpp + LIBRARIES Magnum::Audio + FILES + ../../StbVorbisAudioImporter/Test/mono16.ogg + ../../StbVorbisAudioImporter/Test/stereo8.ogg + ../../StbVorbisAudioImporter/Test/unsupportedChannelCount.ogg + ../../StbVorbisAudioImporter/Test/wrongSignature.ogg) +if(NOT BUILD_PLUGINS_STATIC) + target_include_directories(FfmpegAudioImporterTest PRIVATE $) +else() + target_include_directories(FfmpegAudioImporterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_link_libraries(FfmpegAudioImporterTest PRIVATE FfmpegAudioImporter) +endif() +set_target_properties(FfmpegAudioImporterTest PROPERTIES FOLDER "MagnumPlugins/FfmpegAudioImporter/Test") diff --git a/src/MagnumPlugins/FfmpegAudioImporter/Test/FfmpegImporterTest.cpp b/src/MagnumPlugins/FfmpegAudioImporter/Test/FfmpegImporterTest.cpp new file mode 100644 index 000000000..149e6e822 --- /dev/null +++ b/src/MagnumPlugins/FfmpegAudioImporter/Test/FfmpegImporterTest.cpp @@ -0,0 +1,108 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include + +#include "configure.h" + +namespace Magnum { namespace Audio { namespace Test { + +class FfmpegImporterTest: public TestSuite::Tester { + public: + explicit FfmpegImporterTest(); + + void wrongSize(); + void wrongSignature(); + void unsupportedChannelCount(); + void mono16(); + void stereo8(); + + /* Explicitly forbid system-wide plugin dependencies */ + PluginManager::Manager _manager{"nonexistent"}; +}; + +FfmpegImporterTest::FfmpegImporterTest() { + addTests({&FfmpegImporterTest::wrongSignature, + &FfmpegImporterTest::unsupportedChannelCount, + &FfmpegImporterTest::mono16, + &FfmpegImporterTest::stereo8}); + + /* Load the plugin directly from the build tree. Otherwise it's static and + already loaded. */ + #ifdef FFMPEGAUDIOIMPORTER_PLUGIN_FILENAME + CORRADE_INTERNAL_ASSERT(_manager.load(FFMPEGAUDIOIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded); + #endif +} + +void FfmpegImporterTest::wrongSignature() { + std::ostringstream out; + Error redirectError{&out}; + + std::unique_ptr importer = _manager.instantiate("FfmpegAudioImporter"); + CORRADE_VERIFY(!importer->openFile(WRONG_SIGNATURE_OGG_FILE)); + CORRADE_COMPARE(out.str(), "Audio::FfmpegImporter::openData(): the file signature is invalid\n"); +} + +void FfmpegImporterTest::unsupportedChannelCount() { + std::ostringstream out; + Error redirectError{&out}; + + std::unique_ptr importer = _manager.instantiate("FfmpegAudioImporter"); + CORRADE_VERIFY(!importer->openFile(UNSUPPORTED_CHANNEL_COUNT_OGG_FILE)); + CORRADE_COMPARE(out.str(), "Audio::FfmpegImporter::openData(): unsupported channel count 5 with 16 bits per sample\n"); +} + +void FfmpegImporterTest::mono16() { + std::unique_ptr importer = _manager.instantiate("FfmpegAudioImporter"); + CORRADE_VERIFY(importer->openFile(MONO16_OGG_FILE)); + + CORRADE_COMPARE(importer->format(), BufferFormat::MonoFloat); // TODO: why not 16 + CORRADE_COMPARE(importer->frequency(), 96000); + CORRADE_COMPARE_AS(importer->data(), + (Containers::Array{Containers::InPlaceInit, { + '\xcd', '\x0a', '\x2b', '\x0a'}}), + TestSuite::Compare::Container); +} + +void FfmpegImporterTest::stereo8() { + std::unique_ptr importer = _manager.instantiate("FfmpegAudioImporter"); + CORRADE_VERIFY(importer->openFile(STEREO8_OGG_FILE)); + + CORRADE_COMPARE(importer->format(), BufferFormat::StereoFloat); // TODO: why not 16 + CORRADE_COMPARE(importer->frequency(), 96000); + CORRADE_COMPARE_AS(importer->data(), + (Containers::Array{Containers::InPlaceInit, { + '\x3e', '\x19', '\x1d', '\x17'}}), + TestSuite::Compare::Container); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Audio::Test::FfmpegImporterTest) diff --git a/src/MagnumPlugins/FfmpegAudioImporter/Test/configure.h.cmake b/src/MagnumPlugins/FfmpegAudioImporter/Test/configure.h.cmake new file mode 100644 index 000000000..32a9cbfb8 --- /dev/null +++ b/src/MagnumPlugins/FfmpegAudioImporter/Test/configure.h.cmake @@ -0,0 +1,30 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#cmakedefine FFMPEGAUDIOIMPORTER_PLUGIN_FILENAME "${FFMPEGAUDIOIMPORTER_PLUGIN_FILENAME}" +#define MONO16_OGG_FILE "${MONO16_OGG_FILE}" +#define STEREO8_OGG_FILE "${STEREO8_OGG_FILE}" +#define UNSUPPORTED_CHANNEL_COUNT_OGG_FILE "${UNSUPPORTED_CHANNEL_COUNT_OGG_FILE}" +#define WRONG_SIGNATURE_OGG_FILE "${WRONG_SIGNATURE_OGG_FILE}" diff --git a/src/MagnumPlugins/FfmpegAudioImporter/configure.h.cmake b/src/MagnumPlugins/FfmpegAudioImporter/configure.h.cmake new file mode 100644 index 000000000..548a6a4c2 --- /dev/null +++ b/src/MagnumPlugins/FfmpegAudioImporter/configure.h.cmake @@ -0,0 +1,26 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#cmakedefine MAGNUM_FFMPEGAUDIOIMPORTER_BUILD_STATIC diff --git a/src/MagnumPlugins/FfmpegAudioImporter/importStaticPlugin.cpp b/src/MagnumPlugins/FfmpegAudioImporter/importStaticPlugin.cpp new file mode 100644 index 000000000..58edd3348 --- /dev/null +++ b/src/MagnumPlugins/FfmpegAudioImporter/importStaticPlugin.cpp @@ -0,0 +1,35 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "MagnumPlugins/FfmpegAudioImporter/configure.h" + +#ifdef MAGNUM_STBVORBISAUDIOIMPORTER_BUILD_STATIC +#include + +static int magnumFfmpegAudioImporterStaticImporter() { + CORRADE_PLUGIN_IMPORT(FfmpegAudioImporter) + return 1; +} CORRADE_AUTOMATIC_INITIALIZER(magnumFfmpegAudioImporterStaticImporter) +#endif