diff --git a/.travis.yml b/.travis.yml index b1ccb736..275f6ff0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,14 @@ matrix: - libogg-dev - libvorbis-dev - libflac++-dev + - gstreamer1.0-libav + - gstreamer1.0-plugins-base + - gstreamer1.0-plugins-good + - gstreamer1.0-plugins-ugly + - gstreamer1.0-plugins-bad + - gstreamer1.0-plugins-bad-faad + - libgstreamer1.0-dev + - libgstreamer-plugins-base1.0-dev env: MATRIX_EVAL="CONFIG=Release && CXX=g++-5" diff --git a/audiostream/CMakeLists.txt b/audiostream/CMakeLists.txt index 55c04341..adf52b81 100644 --- a/audiostream/CMakeLists.txt +++ b/audiostream/CMakeLists.txt @@ -7,7 +7,7 @@ option( NIMEDIA_ENABLE_AIFF_ENCODING "Enable ni-media aiff encoding" ON ) option( NIMEDIA_ENABLE_FLAC_DECODING "Enable ni-media flac decoding" ON ) option( NIMEDIA_ENABLE_OGG_DECODING "Enable ni-media ogg decoding" ON ) -if ( APPLE OR WIN32 ) +if ( APPLE OR WIN32 OR LINUX ) option( NIMEDIA_ENABLE_MP3_DECODING "Enable ni-media mp3 decoding" ON ) option( NIMEDIA_ENABLE_MP4_DECODING "Enable ni-media mp4 decoding" ON ) else() @@ -21,7 +21,7 @@ else() option( NIMEDIA_ENABLE_ITUNES_DECODING "Enable ni-media iTunes decoding" OFF ) endif() -if( WIN32 ) +if( WIN32 OR LINUX ) option( NIMEDIA_ENABLE_WMA_DECODING "Enable ni-media wma decoding" ON ) else() option( NIMEDIA_ENABLE_WMA_DECODING "Enable ni-media wma decoding" OFF ) @@ -80,6 +80,7 @@ endif() set( COMPILE_WITH_COREAUDIO DONT_COMPILE) set( COMPILE_WITH_MEDIA_FOUNDATION DONT_COMPILE) +set( COMPILE_WITH_GSTREAMER DONT_COMPILE) #----------------------------------------------------------------------------------------------------------------------- # dependencies @@ -143,6 +144,27 @@ if( NIMEDIA_ENABLE_MP3_DECODING OR NIMEDIA_ENABLE_MP4_DECODING OR NIMEDIA_ENABLE list(APPEND codec_libraries mfplat.lib mfreadwrite.lib mfuuid.lib Propsys.lib) + elseif( LINUX ) + + set(COMPILE_WITH_GSTREAMER) + + find_package(Glib REQUIRED) + find_package(GStreamer REQUIRED) + find_package(GObject REQUIRED) + + if ( NOT TARGET GSTREAMER::gstreamer ) + message(FATAL_ERROR + "You are building ni-media with GStreamer decoding support but the required gstreamer-1.0 library was not found\n" + "Make sure library can be found or disable GSTREAMER decoding by setting:\n" + " * NIMEDIA_ENABLE_MP3_DECODING = OFF\n" + " * NIMEDIA_ENABLE_MP4_DECODING = OFF\n" + " * NIMEDIA_ENABLE_WMA_DECODING = OFF\n") + endif() + + list(APPEND codec_libraries GSTREAMER::gstreamer) + list(APPEND codec_libraries glib-2.0) + list(APPEND codec_libraries gobject-2.0) + else() message(FATAL_ERROR @@ -242,6 +264,7 @@ add_src_file (FILES_media_audio_source "src/ni/media/audio/source/container_sou add_src_file (FILES_media_audio_source "src/ni/media/audio/source/core_audio_file_source.cpp" ${COMPILE_WITH_COREAUDIO} WITH_HEADER ) add_src_file (FILES_media_audio_source "src/ni/media/audio/source/media_foundation_helper.h" ${COMPILE_WITH_MEDIA_FOUNDATION} ) add_src_file (FILES_media_audio_source "src/ni/media/audio/source/media_foundation_file_source.cpp" ${COMPILE_WITH_MEDIA_FOUNDATION} WITH_HEADER ) +add_src_file (FILES_media_audio_source "src/ni/media/audio/source/gstreamer_file_source.cpp" ${COMPILE_WITH_GSTREAMER} WITH_HEADER ) add_src_file (FILES_media_audio_source "src/ni/media/audio/source/aiff_source.h" ${COMPILE_WITH_AIFF_DECODING} ) add_src_file (FILES_media_audio_source "src/ni/media/audio/source/aiff_file_source.h" ${COMPILE_WITH_AIFF_DECODING} ) add_src_file (FILES_media_audio_source "src/ni/media/audio/source/aiff_vector_source.h" ${COMPILE_WITH_AIFF_DECODING} ) diff --git a/audiostream/src/ni/media/audio/source/gstreamer_file_source.cpp b/audiostream/src/ni/media/audio/source/gstreamer_file_source.cpp new file mode 100644 index 00000000..b3625b6a --- /dev/null +++ b/audiostream/src/ni/media/audio/source/gstreamer_file_source.cpp @@ -0,0 +1,349 @@ +// +// Copyright (c) 2017 Native Instruments GmbH, Berlin +// +// 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 "gstreamer_file_source.h" + +#include +#include + + +#include + +#include +#include + +namespace detail +{ +template +struct RingBuffer +{ + static constexpr size_t size = s; + static constexpr size_t mask = ( s - 1 ); + std::array m_buffer; + uint64_t m_write_head = 0; + uint64_t m_read_head = 0; + + void push( const T* data, size_t count ) + { + auto filled = m_write_head - m_read_head; + auto space = s - filled; + assert( count <= space ); + + if ( count ) + { + auto idx = m_write_head & mask; + auto space = size - idx; + auto for_now = std::min( space, count ); + std::copy( data, data + for_now, m_buffer.data() + idx ); + m_write_head += for_now; + push( data + for_now, count - for_now ); + } + } + + size_t pull( T* data, size_t count ) + { + count = std::min( count, m_write_head - m_read_head ); + + if ( count ) + { + auto idx = m_read_head & mask; + auto space = size - idx; + auto for_now = std::min( space, count ); + std::copy( m_buffer.data() + idx, m_buffer.data() + idx + for_now, data ); + m_read_head += for_now; + return for_now + pull( data + for_now, count - for_now ); + } + return 0; + } + + void flush() + { + m_read_head = m_write_head = 0; + } +}; +} + +//---------------------------------------------------------------------------------------------------------------------- +gstreamer_file_source::gstreamer_file_source( const std::string& path, + audio::ifstream_info::container_type container, + size_t stream ) +: m_pipeline( nullptr, gst_object_unref ) +, m_sink( nullptr, gst_object_unref ) +, m_ring_buffer( std::make_unique() ) +{ + init_gstreamer(); + setup_source( path, container ); +} + +//---------------------------------------------------------------------------------------------------------------------- + +gstreamer_file_source::~gstreamer_file_source() +{ + gst_element_set_state( m_pipeline.get(), GST_STATE_NULL ); + wait_for_async_operation(); + m_pipeline.reset(); +} + +//---------------------------------------------------------------------------------------------------------------------- + +GstState gstreamer_file_source::wait_for_async_operation() +{ + GstState current_state = GST_STATE_VOID_PENDING; + GstState pending_state = GST_STATE_VOID_PENDING; + + while ( gst_element_get_state( m_pipeline.get(), ¤t_state, &pending_state, GST_MSECOND ) + == GST_STATE_CHANGE_ASYNC ) + { + g_main_context_iteration( nullptr, FALSE ); + } + + return current_state; +} + +//---------------------------------------------------------------------------------------------------------------------- + +void gstreamer_file_source::init_gstreamer() +{ + static bool gstreamer_initialized = false; + + if ( !std::exchange( gstreamer_initialized, true ) ) + { + gst_init( 0, nullptr ); + } +} + +//---------------------------------------------------------------------------------------------------------------------- + +void gstreamer_file_source::setup_source( const std::string& path, audio::ifstream_info::container_type container ) +{ + m_sink.reset( prepare_pipeline( path ) ); + auto sinkpad = gst_element_get_static_pad( m_sink.get(), "sink" ); + auto caps = gst_pad_get_current_caps( sinkpad ); + auto caps_struct = gst_caps_get_structure( caps, 0 ); + fill_format_info( caps_struct, container ); +} + +//---------------------------------------------------------------------------------------------------------------------- + +GstElement* gstreamer_file_source::prepare_pipeline( const std::string& path ) +{ + m_pipeline.reset( gst_pipeline_new( "pipeline" ) ); + + GstElement* source = gst_element_factory_make( "filesrc", "source" ); + GstElement* decodebin = gst_element_factory_make( "decodebin", "decoder" ); + GstElement* queue = gst_element_factory_make( "queue", "queue" ); + GstElement* sink = gst_element_factory_make( "appsink", "sink" ); + + gst_bin_add_many( GST_BIN( m_pipeline.get() ), source, decodebin, queue, gst_object_ref( sink ), nullptr ); + + gst_element_link_many( source, decodebin, NULL ); + gst_element_link_many( queue, sink, NULL ); + + g_object_set( source, "location", path.c_str(), NULL ); + g_object_set( sink, "sync", FALSE, NULL ); + + g_signal_connect( decodebin, "pad-added", G_CALLBACK( &onPadAdded ), queue ); + + preroll_pipeline(); + return sink; +} + +//---------------------------------------------------------------------------------------------------------------------- + +void gstreamer_file_source::preroll_pipeline() +{ + gst_element_set_state( m_pipeline.get(), GST_STATE_PAUSED ); + + if ( wait_for_async_operation() != GST_STATE_PAUSED ) + throw std::runtime_error( "gstreamer_file_source: pipeline doesn't preroll into paused state" ); + + gst_element_set_state( m_pipeline.get(), GST_STATE_PLAYING ); + + if ( wait_for_async_operation() != GST_STATE_PLAYING ) + throw std::runtime_error( "gstreamer_file_source: pipeline doesn't preroll into playing state" ); +} + +//---------------------------------------------------------------------------------------------------------------------- + +void gstreamer_file_source::fill_format_info( GstStructure* caps_struct, + audio::ifstream_info::container_type container ) +{ + using namespace std::chrono; + + m_info.container( container ); + m_info.codec( audio::ifstream_info::codec_type::mp3 ); + m_info.lossless( false ); + + int sample_rate = 0; + if ( !gst_structure_get_int( caps_struct, "rate", &sample_rate ) ) + throw std::runtime_error( "gstreamer_file_source: could not query sample rate from gstreamer" ); + + m_info.sample_rate( sample_rate ); + + gint64 num_frames = 0; + if ( gst_element_query_duration( m_pipeline.get(), GST_FORMAT_DEFAULT, &num_frames ) && num_frames >= 0 ) + { + m_info.num_frames( size_t( num_frames ) ); + } + else // retrieving num_frames failed: fallback to retrieve time information instead (less precise) + { + gint64 duration_ns = 0; // in nanoseconds + if ( !gst_element_query_duration( m_pipeline.get(), GST_FORMAT_TIME, &duration_ns ) ) + throw std::runtime_error( "gstreamer_file_source: could not query duration from gstreamer" ); + + m_info.num_frames( size_t( duration( nanoseconds( duration_ns ) ).count() * sample_rate ) ); + } + + int num_channels = 0; + if ( !gst_structure_get_int( caps_struct, "channels", &num_channels ) ) + throw std::runtime_error( "gstreamer_file_source: could not query number of channels from gstreamer" ); + + m_info.num_channels( num_channels ); + + m_info.format( create_runtime_format( caps_struct ) ); +} + +//---------------------------------------------------------------------------------------------------------------------- + +pcm::runtime_format gstreamer_file_source::create_runtime_format( GstStructure* caps_struct ) +{ + if ( auto format = gst_structure_get_string( caps_struct, "format" ) ) + { + pcm::number_type number_type = gst_format_char_to_number_type( format[0] ); + auto srcDepth = std::atol( format + 1 ); + auto endian = ( strcmp( format, "BE" ) == 0 ) ? pcm::big_endian : pcm::little_endian; + return pcm::runtime_format( number_type, srcDepth, endian ); + } + throw std::runtime_error( "gstreamer_file_source: could not get runtime format from gstreamer caps" ); +} + +//---------------------------------------------------------------------------------------------------------------------- + +pcm::number_type gstreamer_file_source::gst_format_char_to_number_type( const gchar format ) +{ + if ( format == 'U' ) + return pcm::unsigned_integer; + else if ( format == 'F' ) + return pcm::floating_point; + + return pcm::signed_integer; +} + +//---------------------------------------------------------------------------------------------------------------------- + +void gstreamer_file_source::onPadAdded( GstElement* element, GstPad* pad, GstElement* sink ) +{ + tGstPtr sinkpad( gst_element_get_static_pad( sink, "sink" ), gst_object_unref ); + + if ( gst_pad_is_linked( sinkpad.get() ) ) + return; // already linked + + std::unique_ptr caps(gst_pad_get_current_caps(pad), gst_caps_unref); + auto s = gst_caps_get_structure( caps.get(), 0 ); + auto name = gst_structure_get_name( s ); + + if ( !g_str_has_prefix( name, "audio/" ) ) + return; // not the kind of pad we are looking for, maybe video? + + auto result = gst_pad_link( pad, sinkpad.get() ); + + if ( result != GST_PAD_LINK_OK ) + throw std::runtime_error( "gstreamer_file_source: could not link pad" ); +} + +//---------------------------------------------------------------------------------------------------------------------- + +std::streampos gstreamer_file_source::seek( offset_type off, BOOST_IOS::seekdir way ) +{ + assert( 0 == off % m_info.bytes_per_frame() ); + + const auto beg = std::streampos( 0 ); + const auto end = std::streampos( info().num_bytes() ); + const auto pos = absolute_position( m_position, beg, end, off, way ); + + if ( m_position != pos ) + { + if ( gst_element_seek_simple( m_pipeline.get(), + GST_FORMAT_BYTES, + ( GstSeekFlags )( GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE ), + (gint64) pos ) ) + { + m_ring_buffer->flush(); + m_position = pos; + } + else + { + return -1; + } + } + + return m_position; +} + +//---------------------------------------------------------------------------------------------------------------------- + +std::streamsize gstreamer_file_source::read( char* dst, std::streamsize numBytesRequested ) +{ + return recursive_read( dst, numBytesRequested ); +} + +//---------------------------------------------------------------------------------------------------------------------- + +std::streamsize gstreamer_file_source::recursive_read( char* dst, std::streamsize numBytesRequested ) +{ + auto read_bytes = m_ring_buffer->pull( dst, numBytesRequested ); + + dst += read_bytes; + numBytesRequested -= read_bytes; + m_position += read_bytes; + + if ( numBytesRequested ) + { + GstSample* samplePtr = nullptr; + g_signal_emit_by_name( m_sink.get(), "try-pull-sample", 0, &samplePtr, nullptr ); + + if ( samplePtr ) + { + tGstPtr sample( samplePtr, (GUnref) gst_sample_unref ); + auto buffer = gst_sample_get_buffer( sample.get() ); + + GstMapInfo mapped; + + if ( gst_buffer_map( buffer, &mapped, GST_MAP_READ ) ) + { + auto for_now = std::min( mapped.size, numBytesRequested ); + std::copy( mapped.data, mapped.data + for_now, dst ); + read_bytes += for_now; + dst += for_now; + numBytesRequested -= for_now; + m_position += for_now; + + m_ring_buffer->push( (char*) mapped.data + for_now, mapped.size - for_now ); + gst_buffer_unmap( buffer, &mapped ); + + return read_bytes + recursive_read( dst, numBytesRequested ); + } + } + } + + return read_bytes; +} diff --git a/audiostream/src/ni/media/audio/source/gstreamer_file_source.h b/audiostream/src/ni/media/audio/source/gstreamer_file_source.h new file mode 100644 index 00000000..3fffe6ca --- /dev/null +++ b/audiostream/src/ni/media/audio/source/gstreamer_file_source.h @@ -0,0 +1,85 @@ +// +// Copyright (c) 2017 Native Instruments GmbH, Berlin +// +// 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. +// + +#pragma once + +#include +#include + +#include +#include + +namespace detail +{ +template +struct RingBuffer; +} + +class gstreamer_file_source +{ +public: + //---------------------------------------------------------------------------------------------------------------------- + + using offset_type = boost::iostreams::stream_offset; + + //---------------------------------------------------------------------------------------------------------------------- + + explicit gstreamer_file_source( const std::string& path, + audio::ifstream_info::container_type container, + size_t stream = 0 ); + ~gstreamer_file_source(); + + audio::ifstream_info info() const + { + return m_info; + } + + std::streampos seek( offset_type, BOOST_IOS::seekdir ); + std::streamsize read( char*, std::streamsize ); + +private: + static void init_gstreamer(); + static pcm::runtime_format create_runtime_format( GstStructure* caps_struct ); + static pcm::number_type gst_format_char_to_number_type( const gchar format ); + + void setup_source( const std::string& path, audio::ifstream_info::container_type container ); + GstElement* prepare_pipeline( const std::string& path ); + void preroll_pipeline(); + GstState wait_for_async_operation(); + void fill_format_info( GstStructure* caps_struct, audio::ifstream_info::container_type container ); + + std::streamsize recursive_read( char* dst, std::streamsize numBytesRequested ); + + static void onPadAdded( GstElement* element, GstPad* pad, GstElement* sink ); + + audio::ifstream_info m_info; + using GUnref = void ( * )( gpointer ); + template + using tGstPtr = std::unique_ptr; + + tGstPtr m_pipeline; + tGstPtr m_sink; + + using RingBuffer = detail::RingBuffer; + std::unique_ptr m_ring_buffer; + int64_t m_position = 0; +}; diff --git a/audiostream/src/ni/media/audio/source/mp3_file_source.cpp b/audiostream/src/ni/media/audio/source/mp3_file_source.cpp index 09cddf1c..e22f27ec 100644 --- a/audiostream/src/ni/media/audio/source/mp3_file_source.cpp +++ b/audiostream/src/ni/media/audio/source/mp3_file_source.cpp @@ -26,6 +26,8 @@ #include "core_audio_file_source.h" #elif BOOST_OS_WINDOWS #include "media_foundation_file_source.h" +#elif BOOST_OS_LINUX +#include "gstreamer_file_source.h" #endif //---------------------------------------------------------------------------------------------------------------------- @@ -55,6 +57,8 @@ void mp3_file_source::open( const std::string& path ) m_impl.reset( new core_audio_file_source( path, audio::ifstream_info::container_type::mp3 ) ); #elif BOOST_OS_WINDOWS m_impl.reset( new media_foundation_file_source( path, audio::ifstream_info::container_type::mp3 ) ); +#elif BOOST_OS_LINUX + m_impl.reset( new gstreamer_file_source( path, audio::ifstream_info::container_type::mp3 ) ); #endif } diff --git a/audiostream/src/ni/media/audio/source/mp3_file_source.h b/audiostream/src/ni/media/audio/source/mp3_file_source.h index e3340d20..39dc7eb3 100644 --- a/audiostream/src/ni/media/audio/source/mp3_file_source.h +++ b/audiostream/src/ni/media/audio/source/mp3_file_source.h @@ -61,5 +61,7 @@ class mp3_file_source std::unique_ptr m_impl; #elif BOOST_OS_WINDOWS std::unique_ptr m_impl; +#elif BOOST_OS_LINUX + std::unique_ptr m_impl; #endif }; diff --git a/audiostream/src/ni/media/audio/source/mp4_file_source.cpp b/audiostream/src/ni/media/audio/source/mp4_file_source.cpp index c68f9db0..259fae63 100644 --- a/audiostream/src/ni/media/audio/source/mp4_file_source.cpp +++ b/audiostream/src/ni/media/audio/source/mp4_file_source.cpp @@ -26,6 +26,8 @@ #include "core_audio_file_source.h" #elif BOOST_OS_WINDOWS #include "media_foundation_file_source.h" +#elif BOOST_OS_LINUX +#include "gstreamer_file_source.h" #endif //---------------------------------------------------------------------------------------------------------------------- @@ -55,6 +57,8 @@ void mp4_file_source::open( const std::string& path, size_t stream ) m_impl.reset( new core_audio_file_source( path, audio::ifstream_info::container_type::mp4, stream ) ); #elif BOOST_OS_WINDOWS m_impl.reset( new media_foundation_file_source( path, audio::ifstream_info::container_type::mp4, stream ) ); +#elif BOOST_OS_LINUX + m_impl.reset( new gstreamer_file_source( path, audio::ifstream_info::container_type::mp4 ) ); #endif } diff --git a/audiostream/src/ni/media/audio/source/mp4_file_source.h b/audiostream/src/ni/media/audio/source/mp4_file_source.h index e5fe4679..26ec3b91 100644 --- a/audiostream/src/ni/media/audio/source/mp4_file_source.h +++ b/audiostream/src/ni/media/audio/source/mp4_file_source.h @@ -61,5 +61,7 @@ class mp4_file_source std::unique_ptr m_impl; #elif BOOST_OS_WINDOWS std::unique_ptr m_impl; +#elif BOOST_OS_LINUX + std::unique_ptr m_impl; #endif }; diff --git a/audiostream/src/ni/media/audio/source/wma_file_source.cpp b/audiostream/src/ni/media/audio/source/wma_file_source.cpp index aa4ddec7..61821808 100644 --- a/audiostream/src/ni/media/audio/source/wma_file_source.cpp +++ b/audiostream/src/ni/media/audio/source/wma_file_source.cpp @@ -22,7 +22,11 @@ #include +#if BOOST_OS_WINDOWS #include "media_foundation_file_source.h" +#elif BOOST_OS_LINUX +#include "gstreamer_file_source.h" +#endif //---------------------------------------------------------------------------------------------------------------------- @@ -47,7 +51,11 @@ wma_file_source::wma_file_source( const std::string& path ) void wma_file_source::open( const std::string& path ) { +#if BOOST_OS_WINDOWS m_impl.reset( new media_foundation_file_source( path, audio::ifstream_info::container_type::wma ) ); +#elif BOOST_OS_LINUX + m_impl.reset( new gstreamer_file_source( path, audio::ifstream_info::container_type::wma ) ); +#endif } //---------------------------------------------------------------------------------------------------------------------- diff --git a/audiostream/src/ni/media/audio/source/wma_file_source.h b/audiostream/src/ni/media/audio/source/wma_file_source.h index 30c09a6b..050a9701 100644 --- a/audiostream/src/ni/media/audio/source/wma_file_source.h +++ b/audiostream/src/ni/media/audio/source/wma_file_source.h @@ -26,6 +26,7 @@ #include #include +#include #include @@ -57,5 +58,9 @@ class wma_file_source auto info() const -> info_type; private: +#if BOOST_OS_WINDOWS std::unique_ptr m_impl; +#elif BOOST_OS_LINUX + std::unique_ptr m_impl; +#endif }; diff --git a/cmake/FindGObject.cmake b/cmake/FindGObject.cmake new file mode 100644 index 00000000..afb8cc8d --- /dev/null +++ b/cmake/FindGObject.cmake @@ -0,0 +1,5 @@ +find_package (PkgConfig REQUIRED) +pkg_check_modules (GOBJECT2 REQUIRED gobject-2.0) + +include_directories (${GOBJECT2_INCLUDE_DIRS}) +link_directories (${GOBJECT2_LIBRARY_DIRS}) diff --git a/cmake/FindGStreamer.cmake b/cmake/FindGStreamer.cmake new file mode 100644 index 00000000..61b1df45 --- /dev/null +++ b/cmake/FindGStreamer.cmake @@ -0,0 +1,23 @@ +include(FindPackageHandleStandardArgs) + +find_package(PkgConfig REQUIRED) + +pkg_check_modules (GSTREAMER REQUIRED gstreamer-1.0) +find_library(GSTREAMER_LIBRARY NAMES gstreamer-1.0) + +find_package_handle_standard_args(GSTREAMER DEFAULT_MSG GSTREAMER_INCLUDE_DIRS GSTREAMER_LIBRARY) +mark_as_advanced(GSTREAMER_INCLUDE_DIRS GSTREAMER_LIBRARY) + + +if( GSTREAMER_FOUND ) + if( NOT TARGET GSTREAMER::gstreamer ) + add_library(GSTREAMER::gstreamer SHARED IMPORTED) + set_target_properties(GSTREAMER::gstreamer PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION ${GSTREAMER_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES "${GSTREAMER_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES "${GSTREAMER_LIBRARY}" + ) + endif() +endif() + diff --git a/cmake/FindGlib.cmake b/cmake/FindGlib.cmake new file mode 100644 index 00000000..383f0712 --- /dev/null +++ b/cmake/FindGlib.cmake @@ -0,0 +1,5 @@ +find_package (PkgConfig REQUIRED) +pkg_check_modules (GLIB2 REQUIRED glib-2.0) + +include_directories (${GLIB2_INCLUDE_DIRS}) +link_directories (${GLIB2_LIBRARY_DIRS})