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;