From d409ffcb9191044f302cb44b26d72c9b4e0a828f Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Wed, 7 Jan 2015 14:14:55 +0000 Subject: [PATCH] From Javier Taibo, "I have found that since version 1.1, FFMPEG changed the way audio streams are retrieved, from packed to planar format. SDL interprets packed audio, as is used in the osgmovie example. To make the audio work when the OSGffmpeg plug-in is compiled against recent FFMPEG versions, FFmpegDecoderAudio must check for planar formats and in these cases request the samples as packed. This way all works as before. It can be checked with osgmovie example application. $ osgmovie --audio movie.avi.ffmpeg FFmpegImageStream::open audio failed, audio stream will be disabled: unknown audio format With the attached FFmpegDecoderAudio.cpp, audio sounds correctly. I am also attaching a modified version of FindFFmpeg.cmake that allows to set as FFMPEG_DIR the ffmpeg compiled in the source directory structure. It should not break anything as it only adds some additional search paths. " git-svn-id: http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk@14654 16af8721-9629-0410-8352-f15c8da7e697 --- CMakeModules/FindFFmpeg.cmake | 5 +- src/osgPlugins/ffmpeg/CMakeLists.txt | 21 +- src/osgPlugins/ffmpeg/FFmpegDecoder.cpp | 14 +- src/osgPlugins/ffmpeg/FFmpegDecoderAudio.cpp | 251 ++++++++++++------- src/osgPlugins/ffmpeg/FFmpegDecoderAudio.hpp | 32 ++- src/osgPlugins/ffmpeg/FFmpegHeaders.hpp | 30 ++- src/osgPlugins/ffmpeg/FFmpegImageStream.cpp | 17 +- src/osgPlugins/ffmpeg/FFmpegImageStream.hpp | 2 + src/osgPlugins/ffmpeg/ReaderWriterFFmpeg.cpp | 19 ++ 9 files changed, 270 insertions(+), 121 deletions(-) diff --git a/CMakeModules/FindFFmpeg.cmake b/CMakeModules/FindFFmpeg.cmake index 6521783c4..ce3b11a51 100644 --- a/CMakeModules/FindFFmpeg.cmake +++ b/CMakeModules/FindFFmpeg.cmake @@ -129,11 +129,14 @@ FFMPEG_FIND(LIBAVFORMAT avformat avformat.h) FFMPEG_FIND(LIBAVDEVICE avdevice avdevice.h) FFMPEG_FIND(LIBAVCODEC avcodec avcodec.h) FFMPEG_FIND(LIBAVUTIL avutil avutil.h) +FFMPEG_FIND(LIBSWRESAMPLE swresample swresample.h) +FFMPEG_FIND(LIBAVRESAMPLE avresample avresample.h) FFMPEG_FIND(LIBSWSCALE swscale swscale.h) # not sure about the header to look for here. SET(FFMPEG_FOUND "NO") # Note we don't check FFMPEG_LIBSWSCALE_FOUND here, it's optional. -IF (FFMPEG_LIBAVFORMAT_FOUND AND FFMPEG_LIBAVDEVICE_FOUND AND FFMPEG_LIBAVCODEC_FOUND AND FFMPEG_LIBAVUTIL_FOUND AND STDINT_OK) +IF (FFMPEG_LIBAVFORMAT_FOUND AND FFMPEG_LIBAVDEVICE_FOUND AND FFMPEG_LIBAVCODEC_FOUND AND FFMPEG_LIBAVUTIL_FOUND AND STDINT_OK + AND ( FFMPEG_LIBSWRESAMPLE_FOUND OR FFMPEG_LIBAVRESAMPLE_FOUND ) ) SET(FFMPEG_FOUND "YES") diff --git a/src/osgPlugins/ffmpeg/CMakeLists.txt b/src/osgPlugins/ffmpeg/CMakeLists.txt index 48349787e..6109f9164 100644 --- a/src/osgPlugins/ffmpeg/CMakeLists.txt +++ b/src/osgPlugins/ffmpeg/CMakeLists.txt @@ -1,7 +1,5 @@ INCLUDE_DIRECTORIES( ${FFMPEG_INCLUDE_DIRS} ) LINK_DIRECTORIES(${FFMPEG_LIBRARY_DIRS}) -SET(TARGET_EXTERNAL_LIBRARIES ${FFMPEG_LIBRARIES} ) - IF(FFMPEG_LIBSWSCALE_FOUND) @@ -9,10 +7,27 @@ IF(FFMPEG_LIBSWSCALE_FOUND) ADD_DEFINITIONS(-DUSE_SWSCALE) - SET(TARGET_EXTERNAL_LIBRARIES ${FFMPEG_LIBRARIES} ${FFMPEG_LIBSWSCALE_LIBRARIES}) + SET(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${FFMPEG_LIBSWSCALE_LIBRARIES}) ENDIF() +IF(FFMPEG_LIBSWRESAMPLE_FOUND) + INCLUDE_DIRECTORIES( ${FFMPEG_LIBSWRESAMPLE_INCLUDE_DIRS} ${FFMPEG_LIBSWRESAMPLE_INCLUDE_DIRS}/libswresample ) + + ADD_DEFINITIONS(-DUSE_SWRESAMPLE) + + SET(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${FFMPEG_LIBSWRESAMPLE_LIBRARIES}) +ENDIF() +IF(FFMPEG_LIBAVRESAMPLE_FOUND) + INCLUDE_DIRECTORIES( ${FFMPEG_LIBAVRESAMPLE_INCLUDE_DIRS} ${FFMPEG_LIBAVRESAMPLE_INCLUDE_DIRS}/libavresample ) + + ADD_DEFINITIONS(-DUSE_AVRESAMPLE) + + SET(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${FFMPEG_LIBAVRESAMPLE_LIBRARIES}) +ENDIF() + +SET(TARGET_EXTERNAL_LIBRARIES ${FFMPEG_LIBRARIES} ) + ADD_DEFINITIONS(-D__STDC_CONSTANT_MACROS) # MESSAGE("FFMPEG_LIBAVFORMAT_INCLUDE_DIRS = " ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS} ) diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoder.cpp b/src/osgPlugins/ffmpeg/FFmpegDecoder.cpp index 2b803a050..efd9782ea 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoder.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoder.cpp @@ -126,8 +126,15 @@ bool FFmpegDecoder::open(const std::string & filename, FFmpegParameters* paramet m_format_context.reset(p_format_context); // Retrieve stream info - // Only buffer up to one and a half seconds - p_format_context->max_analyze_duration = AV_TIME_BASE * 1.5f; + // Only buffer up to one and a half seconds by default + float max_analyze_duration = 1.5; + AVDictionaryEntry *mad = av_dict_get( *parameters->getOptions(), "mad", NULL, 0 ); + if ( mad ) { + max_analyze_duration = atof(mad->value); + } + p_format_context->max_analyze_duration = AV_TIME_BASE * max_analyze_duration; +// p_format_context->probesize = 100000; + if (avformat_find_stream_info(p_format_context, NULL) < 0) throw std::runtime_error("av_find_stream_info() failed"); @@ -160,9 +167,8 @@ bool FFmpegDecoder::open(const std::string & filename, FFmpegParameters* paramet try { - m_audio_decoder.open(m_audio_stream); + m_audio_decoder.open(m_audio_stream, parameters); } - catch (const std::runtime_error & error) { OSG_WARN << "FFmpegImageStream::open audio failed, audio stream will be disabled: " << error.what() << std::endl; diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.cpp b/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.cpp index a290033eb..c4839483e 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.cpp @@ -13,6 +13,10 @@ #define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 #endif +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1) +#define av_frame_alloc avcodec_alloc_frame +#define av_frame_free avcodec_free_frame +#endif #if LIBAVCODEC_VERSION_MAJOR < 56 #define AV_CODEC_ID_NONE CODEC_ID_NONE @@ -22,65 +26,85 @@ namespace osgFFmpeg { static int decode_audio(AVCodecContext *avctx, int16_t *samples, int *frame_size_ptr, - const uint8_t *buf, int buf_size) + const uint8_t *buf, int buf_size, + SwrContext *swr_context, + int out_sample_rate, + int out_nb_channels, + AVSampleFormat out_sample_format) { -#if LIBAVCODEC_VERSION_MAJOR >= 56 - - AVFrame *frame = av_frame_alloc(); - - if (!frame) return AVERROR(ENOMEM); +#if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR==52 && LIBAVCODEC_VERSION_MINOR>=32) AVPacket avpkt; av_init_packet(&avpkt); avpkt.data = const_cast(buf); avpkt.size = buf_size; - int got_frame = 0; - int result = avcodec_decode_audio4(avctx, frame, &got_frame, &avpkt); + AVFrame *frame = av_frame_alloc(); + int ret, got_frame = 0; - if (result>=0 && got_frame) - { + if (!frame) + return AVERROR(ENOMEM); + + ret = avcodec_decode_audio4(avctx, frame, &got_frame, &avpkt); + + if (ret >= 0 && got_frame) { int ch, plane_size; int planar = av_sample_fmt_is_planar(avctx->sample_fmt); - int data_size = av_samples_get_buffer_size(&plane_size, avctx->channels, frame->nb_samples, avctx->sample_fmt, 1); - if (*frame_size_ptr < data_size) - { + + int out_samples; + // if sample rate changes, number of samples is different + if ( out_sample_rate != avctx->sample_rate ) { +// out_samples = av_rescale_rnd(swr_get_delay(swr_context, avctx->sample_rate) + +// frame->nb_samples, out_sample_rate, avctx->sample_rate, AV_ROUND_UP); + out_samples = av_rescale_rnd(frame->nb_samples, out_sample_rate, avctx->sample_rate, AV_ROUND_UP); + } + else { + out_samples = frame->nb_samples; + } + + int output_data_size = av_samples_get_buffer_size(&plane_size, out_nb_channels, + out_samples, + out_sample_format, 1); + + if (*frame_size_ptr < output_data_size) { av_log(avctx, AV_LOG_ERROR, "output buffer size is too small for " - "the current frame (%d < %d)\n", *frame_size_ptr, data_size); + "the current frame (%d < %d)\n", *frame_size_ptr, output_data_size); av_frame_free(&frame); return AVERROR(EINVAL); } - memcpy(samples, frame->extended_data[0], plane_size); - if (planar && avctx->channels > 1) - { - uint8_t *out = ((uint8_t *)samples) + plane_size; - for (ch = 1; ch < avctx->channels; ch++) - { - memcpy(out, frame->extended_data[ch], plane_size); - out += plane_size; + + // if resampling is needed, call swr_convert + if ( swr_context != NULL ) { + + out_samples = swr_convert(swr_context, (uint8_t **)&samples, out_samples, + (const uint8_t **)frame->extended_data, frame->nb_samples); + + // recompute output_data_size following swr_convert result (number of samples actually converted) + output_data_size = av_samples_get_buffer_size(&plane_size, out_nb_channels, + out_samples, + out_sample_format, 1); + } + else { + + memcpy(samples, frame->extended_data[0], plane_size); + + if (planar && avctx->channels > 1) { + uint8_t *out = ((uint8_t *)samples) + plane_size; + for (ch = 1; ch < avctx->channels; ch++) { + memcpy(out, frame->extended_data[ch], plane_size); + out += plane_size; + } } } - *frame_size_ptr = data_size; - } - else - { + + *frame_size_ptr = output_data_size; + + } else { *frame_size_ptr = 0; } - av_frame_free(&frame); + return ret; - return result; - -#elif LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR==52 && LIBAVCODEC_VERSION_MINOR>=32) - - // following code segment copied from ffmpeg's avcodec_decode_audio2() - // implementation to avoid warnings about deprecated function usage. - AVPacket avpkt; - av_init_packet(&avpkt); - avpkt.data = const_cast(buf); - avpkt.size = buf_size; - - return avcodec_decode_audio3(avctx, samples, frame_size_ptr, &avpkt); #else // fallback for older versions of ffmpeg that don't have avcodec_decode_audio3. return avcodec_decode_audio2(avctx, samples, frame_size_ptr, buf, buf_size); @@ -100,9 +124,9 @@ FFmpegDecoderAudio::FFmpegDecoderAudio(PacketQueue & packets, FFmpegClocks & clo m_audio_buf_index(0), m_end_of_stream(false), m_paused(true), - m_exit(false) + m_exit(false), + m_swr_context(NULL) { - } @@ -114,7 +138,7 @@ FFmpegDecoderAudio::~FFmpegDecoderAudio() -void FFmpegDecoderAudio::open(AVStream * const stream) +void FFmpegDecoderAudio::open(AVStream * const stream, FFmpegParameters* parameters) { try { @@ -125,51 +149,60 @@ void FFmpegDecoderAudio::open(AVStream * const stream) m_stream = stream; m_context = stream->codec; - m_frequency = m_context->sample_rate; - m_nb_channels = m_context->channels; + m_in_sample_rate = m_context->sample_rate; + m_in_nb_channels = m_context->channels; + m_in_sample_format = m_context->sample_fmt; - OSG_INFO<<"FFmpegDecoderAudio::open(..), m_nb_channels="<sample_fmt="<sample_fmt<getOptions(), "out_sample_rate", NULL, 0 ); + if ( opt_out_sample_rate ) + m_out_sample_rate = atoi(opt_out_sample_rate->value); + else + m_out_sample_rate = m_in_sample_rate; - switch (m_context->sample_fmt) + AVDictionaryEntry *opt_out_sample_format = av_dict_get( *parameters->getOptions(), "out_sample_format", NULL, 0 ); + if ( opt_out_sample_format ) + m_out_sample_format = (AVSampleFormat) atoi(opt_out_sample_format->value); + else + // always packed, planar formats are evil! + m_out_sample_format = av_get_packed_sample_fmt( m_in_sample_format ); + + AVDictionaryEntry *opt_out_nb_channels = av_dict_get( *parameters->getOptions(), "out_nb_channels", NULL, 0 ); + if ( opt_out_nb_channels ) + m_out_nb_channels = atoi(opt_out_nb_channels->value); + else + m_out_nb_channels = m_in_nb_channels; + + if ( m_in_sample_rate != m_out_sample_rate + || m_in_nb_channels != m_out_nb_channels + || m_in_sample_format != m_out_sample_format ) { - case AV_SAMPLE_FMT_NONE: - throw std::runtime_error("invalid audio format AV_SAMPLE_FMT_NONE"); - case AV_SAMPLE_FMT_U8: - m_sample_format = osg::AudioStream::SAMPLE_FORMAT_U8; - break; - case AV_SAMPLE_FMT_S16: - m_sample_format = osg::AudioStream::SAMPLE_FORMAT_S16; - break; - case AV_SAMPLE_FMT_S32: - m_sample_format = osg::AudioStream::SAMPLE_FORMAT_S32; - break; - case AV_SAMPLE_FMT_FLT: - m_sample_format = osg::AudioStream::SAMPLE_FORMAT_F32; - break; - case AV_SAMPLE_FMT_DBL: - throw std::runtime_error("unhandled audio format AV_SAMPLE_FMT_DBL"); +#if 0 +printf("### CONVERTING from sample format %s TO %s\n\t\tFROM %d TO %d channels\n\t\tFROM %d Hz to %d Hz\n", + av_get_sample_fmt_name(m_in_sample_format), + av_get_sample_fmt_name(m_out_sample_format), + m_in_nb_channels, + m_out_nb_channels, + m_in_sample_rate, + m_out_sample_rate); +#endif + m_swr_context = swr_alloc_set_opts(NULL, + av_get_default_channel_layout(m_out_nb_channels), + m_out_sample_format, + m_out_sample_rate, + av_get_default_channel_layout(m_in_nb_channels), + m_in_sample_format, + m_in_sample_rate, + 0, NULL ); - case AV_SAMPLE_FMT_U8P: - m_sample_format = osg::AudioStream::SAMPLE_FORMAT_U8; - m_context->request_sample_fmt = av_get_packed_sample_fmt( m_context->sample_fmt ); - break; - case AV_SAMPLE_FMT_S16P: - m_sample_format = osg::AudioStream::SAMPLE_FORMAT_S16; - m_context->request_sample_fmt = av_get_packed_sample_fmt( m_context->sample_fmt ); - break; - case AV_SAMPLE_FMT_S32P: - m_sample_format = osg::AudioStream::SAMPLE_FORMAT_S32; - m_context->request_sample_fmt = av_get_packed_sample_fmt( m_context->sample_fmt ); - break; - case AV_SAMPLE_FMT_FLTP: - m_sample_format = osg::AudioStream::SAMPLE_FORMAT_F32; - m_context->request_sample_fmt = av_get_packed_sample_fmt( m_context->sample_fmt ); - break; - case AV_SAMPLE_FMT_DBLP: - throw std::runtime_error("unhandled audio format AV_SAMPLE_FMT_DBLP"); + int err = swr_init(m_swr_context); - default: - throw std::runtime_error("unknown audio format"); + if ( err ) { + char error_string[512]; + av_strerror(err, error_string, 512); + OSG_WARN << "FFmpegDecoderAudio - WARNING: Error initializing resampling context : " << error_string << std::endl; + swr_free(&m_swr_context); + throw std::runtime_error("swr_init() failed");; + } } // Check stream sanity @@ -189,6 +222,10 @@ void FFmpegDecoderAudio::open(AVStream * const stream) // Open codec if (avcodec_open2(m_context, p_codec, NULL) < 0) throw std::runtime_error("avcodec_open() failed"); + + m_context->get_buffer = avcodec_default_get_buffer; + m_context->release_buffer = avcodec_default_release_buffer; + } catch (...) @@ -219,6 +256,7 @@ void FFmpegDecoderAudio::close(bool waitForThreadToExit) if (waitForThreadToExit) join(); } + swr_free(&m_swr_context); } void FFmpegDecoderAudio::setVolume(float volume) @@ -300,7 +338,7 @@ void FFmpegDecoderAudio::fillBuffer(void * const buffer, size_t size) m_audio_buf_index += fill_size; - adjustBufferEndTps(fill_size); + adjustBufferEndPts(fill_size); } } @@ -357,38 +395,37 @@ void FFmpegDecoderAudio::decodeLoop() } - -void FFmpegDecoderAudio::adjustBufferEndTps(const size_t buffer_size) +void FFmpegDecoderAudio::adjustBufferEndPts(const size_t buffer_size) { - int sample_size = nbChannels() * frequency(); + int bytes_per_second = nbChannels() * frequency(); switch (sampleFormat()) { case osg::AudioStream::SAMPLE_FORMAT_U8: - sample_size *= 1; + bytes_per_second *= 1; break; case osg::AudioStream::SAMPLE_FORMAT_S16: - sample_size *= 2; + bytes_per_second *= 2; break; case osg::AudioStream::SAMPLE_FORMAT_S24: - sample_size *= 3; + bytes_per_second *= 3; break; case osg::AudioStream::SAMPLE_FORMAT_S32: - sample_size *= 4; + bytes_per_second *= 4; break; case osg::AudioStream::SAMPLE_FORMAT_F32: - sample_size *= 4; + bytes_per_second *= 4; break; default: throw std::runtime_error("unsupported audio sample format"); } - m_clocks.audioAdjustBufferEndPts(double(buffer_size) / double(sample_size)); + m_clocks.audioAdjustBufferEndPts(double(buffer_size) / double(bytes_per_second)); } @@ -403,7 +440,7 @@ size_t FFmpegDecoderAudio::decodeFrame(void * const buffer, const size_t size) { int data_size = size; - const int bytes_decoded = decode_audio(m_context, reinterpret_cast(buffer), &data_size, m_packet_data, m_bytes_remaining); + const int bytes_decoded = decode_audio(m_context, reinterpret_cast(buffer), &data_size, m_packet_data, m_bytes_remaining, m_swr_context, m_out_sample_rate, m_out_nb_channels, m_out_sample_format); if (bytes_decoded < 0) { @@ -464,5 +501,33 @@ size_t FFmpegDecoderAudio::decodeFrame(void * const buffer, const size_t size) } +/** + * + */ +osg::AudioStream::SampleFormat FFmpegDecoderAudio::sampleFormat() const +{ + switch (m_out_sample_format) + { + case AV_SAMPLE_FMT_NONE: + throw std::runtime_error("invalid audio format AV_SAMPLE_FMT_NONE"); + case AV_SAMPLE_FMT_U8: + return osg::AudioStream::SAMPLE_FORMAT_U8; + break; + case AV_SAMPLE_FMT_S16: + return osg::AudioStream::SAMPLE_FORMAT_S16; + break; + case AV_SAMPLE_FMT_S32: + return osg::AudioStream::SAMPLE_FORMAT_S32; + break; + case AV_SAMPLE_FMT_FLT: + return osg::AudioStream::SAMPLE_FORMAT_F32; + break; + case AV_SAMPLE_FMT_DBL: + throw std::runtime_error("unhandled audio format AV_SAMPLE_FMT_DBL"); + + default: + throw std::runtime_error("unknown audio format"); + } +} } // namespace osgFFmpeg diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.hpp b/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.hpp index abe87817e..9f1b968cc 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoderAudio.hpp @@ -8,14 +8,13 @@ #include "FFmpegClocks.hpp" #include "FFmpegPacket.hpp" +#include "FFmpegParameters.hpp" #include #include "BoundedMessageQueue.hpp" - - namespace osgFFmpeg { @@ -30,10 +29,10 @@ public: FFmpegDecoderAudio(PacketQueue & packets, FFmpegClocks & clocks); ~FFmpegDecoderAudio(); - void open(AVStream * stream); + void open(AVStream * stream, FFmpegParameters* parameters); void pause(bool pause); void close(bool waitForThreadToExit); - + void setVolume(float volume); float getVolume() const; @@ -53,7 +52,7 @@ private: typedef std::vector Buffer; void decodeLoop(); - void adjustBufferEndTps(size_t buffer_size); + void adjustBufferEndPts(size_t buffer_size); size_t decodeFrame(void * buffer, size_t size); @@ -69,9 +68,12 @@ private: size_t m_audio_buf_size; size_t m_audio_buf_index; - int m_frequency; - int m_nb_channels; - osg::AudioStream::SampleFormat m_sample_format; + int m_in_sample_rate; + int m_in_nb_channels; + AVSampleFormat m_in_sample_format; + int m_out_sample_rate; + int m_out_nb_channels; + AVSampleFormat m_out_sample_format; SinkPtr m_audio_sink; @@ -80,6 +82,8 @@ private: bool m_end_of_stream; bool m_paused; volatile bool m_exit; + + SwrContext * m_swr_context; // Sw resampling context }; @@ -94,23 +98,15 @@ inline bool FFmpegDecoderAudio::validContext() const inline int FFmpegDecoderAudio::frequency() const { - return m_frequency; + return m_out_sample_rate; } inline int FFmpegDecoderAudio::nbChannels() const { - return m_nb_channels; + return m_out_nb_channels; } - -inline osg::AudioStream::SampleFormat FFmpegDecoderAudio::sampleFormat() const -{ - return m_sample_format; -} - - - } // namespace osgFFmpeg diff --git a/src/osgPlugins/ffmpeg/FFmpegHeaders.hpp b/src/osgPlugins/ffmpeg/FFmpegHeaders.hpp index 96abcabff..54b08de81 100644 --- a/src/osgPlugins/ffmpeg/FFmpegHeaders.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegHeaders.hpp @@ -11,13 +11,41 @@ extern "C" #include #include +#ifdef USE_AVRESAMPLE // To compile using libav instead of ffmpeg + +# include + +# define SwrContext AVAudioResampleContext +# define swr_init avresample_open +# define swr_free(ctx) avresample_free(ctx) +# define swr_convert(ctx, out, out_size, in, in_size) avresample_convert(ctx, out, 0, out_size, (uint8_t **)in, 0, in_size) +# define swr_alloc_set_opts swr_alloc_set_opts_proxy + +# include + + inline AVAudioResampleContext * swr_alloc_set_opts_proxy(SwrContext *ctx, int64_t out_ch_layout, enum AVSampleFormat out_sample_format, int out_sample_rate, int64_t in_ch_layout, enum AVSampleFormat in_sample_format, int in_sample_rate, int /*log_offset*/, void * /*log_ctx*/) + { + AVAudioResampleContext *avr = avresample_alloc_context(); + av_opt_set_int(avr, "in_channel_layout", in_ch_layout, 0); + av_opt_set_int(avr, "out_channel_layout", out_ch_layout, 0); + av_opt_set_int(avr, "in_sample_rate", in_sample_rate, 0); + av_opt_set_int(avr, "out_sample_rate", out_sample_rate, 0); + av_opt_set_int(avr, "in_sample_fmt", in_sample_format, 0); + av_opt_set_int(avr, "out_sample_fmt", out_sample_format, 0); + return avr; + } + +#else +# include +#endif + #ifndef ANDROID #include #endif #include -#ifdef USE_SWSCALE +#ifdef USE_SWSCALE #include #endif diff --git a/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp b/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp index 592fac8e4..70b350a6c 100644 --- a/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp @@ -8,6 +8,7 @@ #include +#define STREAM_TIMEOUT_IN_SECONDS_TO_CONSIDER_IT_DEAD 10 namespace osgFFmpeg { @@ -17,7 +18,8 @@ namespace osgFFmpeg { FFmpegImageStream::FFmpegImageStream() : m_decoder(0), m_commands(0), - m_frame_published_flag(false) + m_frame_published_flag(false), + _lastUpdateTS(0.) { setOrigin(osg::Image::TOP_LEFT); @@ -219,6 +221,14 @@ void FFmpegImageStream::run() } else done = ! handleCommand(cmd); + + // Security check to detect (and stop) dead streams + if ( _lastUpdateTS > 0. && + osg::Timer::instance()->delta_s(_lastUpdateTS, osg::Timer::instance()->tick()) > STREAM_TIMEOUT_IN_SECONDS_TO_CONSIDER_IT_DEAD ) + { + _status = INVALID; + done = true; + } } else { @@ -238,6 +248,8 @@ void FFmpegImageStream::run() } OSG_NOTICE<<"Finished FFmpegImageStream::run()"<dirty(); #endif + // Store the timestamp of this update. Needed to check dead streams + this_->_lastUpdateTS = osg::Timer::instance()->tick(); + OpenThreads::ScopedLock lock(this_->m_mutex); if (! this_->m_frame_published_flag) diff --git a/src/osgPlugins/ffmpeg/FFmpegImageStream.hpp b/src/osgPlugins/ffmpeg/FFmpegImageStream.hpp index 9c0ace976..63ec06fda 100644 --- a/src/osgPlugins/ffmpeg/FFmpegImageStream.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegImageStream.hpp @@ -81,6 +81,8 @@ namespace osgFFmpeg Condition m_frame_published_cond; bool m_frame_published_flag; double m_seek_time; + + osg::Timer_t _lastUpdateTS; ///< Timestamp for last frame update }; } diff --git a/src/osgPlugins/ffmpeg/ReaderWriterFFmpeg.cpp b/src/osgPlugins/ffmpeg/ReaderWriterFFmpeg.cpp index 6cf4c61a1..4defbb2f2 100644 --- a/src/osgPlugins/ffmpeg/ReaderWriterFFmpeg.cpp +++ b/src/osgPlugins/ffmpeg/ReaderWriterFFmpeg.cpp @@ -96,13 +96,20 @@ public: supportsExtension("3gp", "3G multi-media format"); supportsExtension("sdp", "Session Description Protocol"); supportsExtension("m2ts", "MPEG-2 Transport Stream"); + supportsExtension("ts", "MPEG-2 Transport Stream"); supportsOption("format", "Force setting input format (e.g. vfwcap for Windows webcam)"); supportsOption("pixel_format", "Set pixel format"); supportsOption("frame_size", "Set frame size (e.g. 320x240)"); supportsOption("frame_rate", "Set frame rate (e.g. 25)"); + // WARNING: This option is kept for backwards compatibility only, use out_sample_rate instead! supportsOption("audio_sample_rate", "Set audio sampling rate (e.g. 44100)"); + supportsOption("out_sample_format", "Set the output sample format (e.g. AV_SAMPLE_FMT_S16)"); + supportsOption("out_sample_rate", "Set the output sample rate or frequency in Hz (e.g. 48000)"); + supportsOption("out_nb_channels", "Set the output number of channels (e.g. 2 for stereo)"); supportsOption("context", "AVIOContext* for custom IO"); + supportsOption("mad", "Max analyze duration (seconds)"); + supportsOption("rtsp_transport", "RTSP transport (udp, tcp, udp_multicast or http)"); av_log_set_callback(log_to_osg); @@ -138,10 +145,22 @@ public: return readImageStream(filename, parameters.get()); } +#if 1 + // NOTE: The original code checks parameters->isFormatAvailable() which returns + // false when a format is not explicitly specified. + // In these cases, the extension is used, which is a problem for videos served + // from URLs without an extension + { + ReadResult rr = readImageStream(filename, parameters.get()); + if ( rr.validImage() ) + return rr; + } +#else if (parameters->isFormatAvailable()) { return readImageStream(filename, parameters.get()); } +#endif if (! acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;