diff --git a/CMakeModules/FindFFmpeg.cmake b/CMakeModules/FindFFmpeg.cmake index 9140c4ede..1cccf4e62 100644 --- a/CMakeModules/FindFFmpeg.cmake +++ b/CMakeModules/FindFFmpeg.cmake @@ -11,7 +11,8 @@ #In ffmpeg code, old version use "#include " and newer use "#include " -#In OSG ffmpeg plugin, we use "#include " for compatibility with old version of ffmpeg +#In OSG ffmpeg plugin, we used "#include " for compatibility with old version of ffmpeg +#With the new version of FFmpeg, a file named "time.h" was added that breaks compatability with the old version of ffmpeg. #We have to search the path which contain the header.h (usefull for old version) #and search the path which contain the libname/header.h (usefull for new version) @@ -131,20 +132,17 @@ IF (FFMPEG_LIBAVFORMAT_FOUND AND FFMPEG_LIBAVDEVICE_FOUND AND FFMPEG_LIBAVCODE SET(FFMPEG_FOUND "YES") SET(FFMPEG_INCLUDE_DIRS - ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS} ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS}/libavformat - ${FFMPEG_LIBAVDEVICE_INCLUDE_DIRS} ${FFMPEG_LIBAVDEVICE_INCLUDE_DIRS}/libavdevice - ${FFMPEG_LIBAVCODEC_INCLUDE_DIRS} ${FFMPEG_LIBAVCODEC_INCLUDE_DIRS}/libavcodec - ${FFMPEG_LIBAVUTIL_INCLUDE_DIRS} ${FFMPEG_LIBAVUTIL_INCLUDE_DIRS}/libavutil + ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS} + ${FFMPEG_LIBAVDEVICE_INCLUDE_DIRS} + ${FFMPEG_LIBAVCODEC_INCLUDE_DIRS} + ${FFMPEG_LIBAVUTIL_INCLUDE_DIRS} ) +# Using the new include style for FFmpeg prevents issues with #include IF (FFMPEG_STDINT_INCLUDE_DIR) SET(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} ${FFMPEG_STDINT_INCLUDE_DIR} - ${FFMPEG_STDINT_INCLUDE_DIR}/libavformat - ${FFMPEG_STDINT_INCLUDE_DIR}/libavdevice - ${FFMPEG_STDINT_INCLUDE_DIR}/libavcodec - ${FFMPEG_STDINT_INCLUDE_DIR}/libavutil ) ENDIF() diff --git a/src/osgPlugins/ffmpeg/CMakeLists.txt b/src/osgPlugins/ffmpeg/CMakeLists.txt index b9c0340ed..bfb5f3ba9 100644 --- a/src/osgPlugins/ffmpeg/CMakeLists.txt +++ b/src/osgPlugins/ffmpeg/CMakeLists.txt @@ -13,6 +13,8 @@ IF(FFMPEG_LIBSWSCALE_FOUND) ENDIF() +ADD_DEFINITIONS(-D__STDC_CONSTANT_MACROS) + # MESSAGE("FFMPEG_LIBAVFORMAT_INCLUDE_DIRS = " ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS} ) # MESSAGE("FFMPEG_LIBAVDEVICE_INCLUDE_DIRS = " ${FFMPEG_LIBAVDEVICE_INCLUDE_DIRS} ) # MESSAGE("FFMPEG_LIBAVCODEC_INCLUDE_DIRS = " ${FFMPEG_LIBAVCODEC_INCLUDE_DIRS} ) diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoder.cpp b/src/osgPlugins/ffmpeg/FFmpegDecoder.cpp index 5c6ec2c81..497969f43 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoder.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoder.cpp @@ -28,7 +28,12 @@ namespace osgFFmpeg { - +static std::string AvStrError(int errnum) +{ + char buf[128]; + av_strerror(errnum, buf, sizeof(buf)); + return std::string(buf); +} FFmpegDecoder::FFmpegDecoder() : m_audio_stream(0), @@ -60,25 +65,20 @@ bool FFmpegDecoder::open(const std::string & filename, FFmpegParameters* paramet if (filename.compare(0, 5, "/dev/")==0) { +#ifdef ANDROID + throw std::runtime_error("Device not supported on Android"); +#else avdevice_register_all(); OSG_NOTICE<<"Attempting to stream "<getOptions(), "video_size", "320x240", 0); #else - formatParams.width = 640; - formatParams.height = 480; + av_dict_set(parameters->getOptions(), "video_size", "640x480", 0); #endif - formatParams.time_base.num = 1; - formatParams.time_base.den = 30; + av_dict_set(parameters->getOptions(), "framerate", "1:30", 0); std::string format = "video4linux2"; iformat = av_find_input_format(format.c_str()); @@ -92,7 +92,7 @@ bool FFmpegDecoder::open(const std::string & filename, FFmpegParameters* paramet OSG_NOTICE<<"Failed to find input format: "<getOptions()); if (error != 0) { std::string error_str; @@ -112,34 +112,53 @@ bool FFmpegDecoder::open(const std::string & filename, FFmpegParameters* paramet throw std::runtime_error("av_open_input_file() failed : " + error_str); } +#endif } else { - AVInputFormat* av_format = (parameters ? parameters->getFormat() : 0); - AVFormatParameters* av_params = (parameters ? parameters->getFormatParameter() : 0); - if (av_open_input_file(&p_format_context, filename.c_str(), av_format, 0, av_params) !=0 ) + AVInputFormat* iformat = (parameters ? parameters->getFormat() : 0); + AVIOContext* context = parameters->getContext(); + if (context != NULL) + { + p_format_context = avformat_alloc_context(); + p_format_context->pb = context; + } + if (avformat_open_input(&p_format_context, filename.c_str(), iformat, parameters->getOptions()) != 0) throw std::runtime_error("av_open_input_file() failed"); } m_format_context.reset(p_format_context); // Retrieve stream info - if (av_find_stream_info(p_format_context) < 0) + // Only buffer up to one and a half seconds + p_format_context->max_analyze_duration = AV_TIME_BASE * 1.5f; + if (avformat_find_stream_info(p_format_context, NULL) < 0) throw std::runtime_error("av_find_stream_info() failed"); m_duration = double(m_format_context->duration) / AV_TIME_BASE; - m_start = double(m_format_context->start_time) / AV_TIME_BASE; + if (m_format_context->start_time != AV_NOPTS_VALUE) + m_start = double(m_format_context->start_time) / AV_TIME_BASE; + else + m_start = 0; // TODO move this elsewhere m_clocks.reset(m_start); // Dump info to stderr - dump_format(p_format_context, 0, filename.c_str(), false); + av_dump_format(p_format_context, 0, filename.c_str(), false); // Find and open the first video and audio streams (note that audio stream is optional and only opened if possible) + if ((m_video_index = av_find_best_stream(m_format_context.get(), AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0)) < 0) + throw std::runtime_error("Could not open video stream"); + m_video_stream = m_format_context->streams[m_video_index]; - findVideoStream(); - findAudioStream(); + if ((m_audio_index = av_find_best_stream(m_format_context.get(), AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0)) >= 0) + m_audio_stream = m_format_context->streams[m_audio_index]; + else + { + m_audio_stream = 0; + m_audio_index = std::numeric_limits::max(); + } m_video_decoder.open(m_video_stream); @@ -196,6 +215,7 @@ bool FFmpegDecoder::readNextPacket() return readNextPacketSeeking(); default: + OSG_FATAL << "unknown decoder state " << m_state << std::endl; assert(false); return false; } @@ -230,40 +250,6 @@ void FFmpegDecoder::pause() m_state = PAUSE; } -void FFmpegDecoder::findAudioStream() -{ - for (unsigned int i = 0; i < m_format_context->nb_streams; ++i) - { - if (m_format_context->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO) - { - m_audio_stream = m_format_context->streams[i]; - m_audio_index = i; - return; - } - } - - m_audio_stream = 0; - m_audio_index = std::numeric_limits::max(); -} - - - -void FFmpegDecoder::findVideoStream() -{ - for (unsigned int i = 0; i < m_format_context->nb_streams; ++i) - { - if (m_format_context->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) - { - m_video_stream = m_format_context->streams[i]; - m_video_index = i; - return; - } - } - - throw std::runtime_error("could not find a video stream"); -} - - inline void FFmpegDecoder::flushAudioQueue() { @@ -290,12 +276,15 @@ bool FFmpegDecoder::readNextPacketNormal() bool end_of_stream = false; // Read the next frame packet - if (av_read_frame(m_format_context.get(), &packet) < 0) + int error = av_read_frame(m_format_context.get(), &packet); + if (error < 0) { - if (url_ferror(m_format_context->pb) == 0) + if (error == AVERROR_EOF || url_feof(m_format_context.get()->pb)) end_of_stream = true; - else + else { + OSG_FATAL << "av_read_frame() returned " << AvStrError(error) << std::endl; throw std::runtime_error("av_read_frame() failed"); + } } if (end_of_stream) @@ -314,8 +303,10 @@ bool FFmpegDecoder::readNextPacketNormal() else { // Make the packet data available beyond av_read_frame() logical scope. - if (av_dup_packet(&packet) < 0) + if ((error = av_dup_packet(&packet)) < 0) { + OSG_FATAL << "av_dup_packet() returned " << AvStrError(error) << std::endl; throw std::runtime_error("av_dup_packet() failed"); + } m_pending_packet = FFmpegPacket(packet); } @@ -381,8 +372,11 @@ void FFmpegDecoder::rewindButDontFlushQueues() const int64_t pos = int64_t(m_clocks.getStartTime() * double(AV_TIME_BASE)); const int64_t seek_target = av_rescale_q(pos, AvTimeBaseQ, m_video_stream->time_base); - if (av_seek_frame(m_format_context.get(), m_video_index, seek_target, 0/*AVSEEK_FLAG_BYTE |*/ /*AVSEEK_FLAG_BACKWARD*/) < 0) + int error = 0; + if ((error = av_seek_frame(m_format_context.get(), m_video_index, seek_target, 0/*AVSEEK_FLAG_BYTE |*/ /*AVSEEK_FLAG_BACKWARD*/)) < 0) { + OSG_FATAL << "av_seek_frame returned " << AvStrError(error) << std::endl; throw std::runtime_error("av_seek_frame failed()"); + } m_clocks.rewind(); m_state = REWINDING; @@ -407,8 +401,11 @@ void FFmpegDecoder::seekButDontFlushQueues(double time) m_clocks.setSeekTime(time); - if (av_seek_frame(m_format_context.get(), m_video_index, seek_target, 0/*AVSEEK_FLAG_BYTE |*/ /*AVSEEK_FLAG_BACKWARD*/) < 0) + int error = 0; + if ((error = av_seek_frame(m_format_context.get(), m_video_index, seek_target, 0/*AVSEEK_FLAG_BYTE |*/ /*AVSEEK_FLAG_BACKWARD*/)) < 0) { + OSG_FATAL << "av_seek_frame() returned " << AvStrError(error) << std::endl; throw std::runtime_error("av_seek_frame failed()"); + } m_clocks.seek(time); m_state = SEEKING; diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoder.hpp b/src/osgPlugins/ffmpeg/FFmpegDecoder.hpp index f0212214b..95d638f25 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoder.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoder.hpp @@ -46,8 +46,13 @@ class FormatContextPtr { if (_ptr) { +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53, 17, 0) + OSG_NOTICE<<"Calling avformat_close_input("<<&_ptr<<")"< PacketQueue; - void findAudioStream(); - void findVideoStream(); void flushAudioQueue(); void flushVideoQueue(); bool readNextPacketNormal(); @@ -151,13 +154,13 @@ inline bool FFmpegDecoder::loop() const inline double FFmpegDecoder::creation_time() const { - if(m_format_context) return m_format_context->timestamp; + if(m_format_context) return m_format_context->start_time; else return HUGE_VAL; } inline double FFmpegDecoder::duration() const { - return double(m_format_context->duration) / AV_TIME_BASE; + return double(m_format_context->duration) / AV_TIME_BASE; } inline double FFmpegDecoder::reference() diff --git a/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.cpp b/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.cpp index 53881c138..6dadbcdc8 100644 --- a/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegDecoderVideo.cpp @@ -8,27 +8,6 @@ namespace osgFFmpeg { -static int decode_video(AVCodecContext *avctx, AVFrame *picture, - int *got_picture_ptr, - const uint8_t *buf, int buf_size) -{ -#if LIBAVCODEC_VERSION_MAJOR >= 53 || (LIBAVCODEC_VERSION_MAJOR==52 && LIBAVCODEC_VERSION_MINOR>=32) - // following code segment copied from ffmpeg avcodec_decode_video() implementation - // to avoid warnings about deprecated function usage. - AVPacket avpkt; - av_init_packet(&avpkt); - avpkt.data = const_cast(buf); - avpkt.size = buf_size; - // HACK for CorePNG to decode as normal PNG by default - avpkt.flags = AV_PKT_FLAG_KEY; - - return avcodec_decode_video2(avctx, picture, got_picture_ptr, &avpkt); -#else - // fallback for older versions of ffmpeg that don't have avcodec_decode_video2. - return avcodec_decode_video(avctx, picture, got_picture_ptr, buf, buf_size); -#endif -} - FFmpegDecoderVideo::FFmpegDecoderVideo(PacketQueue & packets, FFmpegClocks & clocks) : m_packets(packets), @@ -105,7 +84,7 @@ void FFmpegDecoderVideo::open(AVStream * const stream) // m_context->flags |= CODEC_FLAG_TRUNCATED; // Open codec - if (avcodec_open(m_context, m_codec) < 0) + if (avcodec_open2(m_context, m_codec, NULL) < 0) throw std::runtime_error("avcodec_open() failed"); // Allocate video frame @@ -113,11 +92,11 @@ void FFmpegDecoderVideo::open(AVStream * const stream) // Allocate converted RGB frame m_frame_rgba.reset(avcodec_alloc_frame()); - m_buffer_rgba[0].resize(avpicture_get_size(PIX_FMT_RGB32, width(), height())); + m_buffer_rgba[0].resize(avpicture_get_size(PIX_FMT_RGB24, width(), height())); m_buffer_rgba[1].resize(m_buffer_rgba[0].size()); // Assign appropriate parts of the buffer to image planes in m_frame_rgba - avpicture_fill((AVPicture *) (m_frame_rgba).get(), &(m_buffer_rgba[0])[0], PIX_FMT_RGB32, width(), height()); + avpicture_fill((AVPicture *) (m_frame_rgba).get(), &(m_buffer_rgba[0])[0], PIX_FMT_RGB24, width(), height()); // Override get_buffer()/release_buffer() from codec context in order to retrieve the PTS of each frame. m_context->opaque = this; @@ -183,7 +162,8 @@ void FFmpegDecoderVideo::decodeLoop() int frame_finished = 0; - const int bytes_decoded = decode_video(m_context, m_frame.get(), &frame_finished, m_packet_data, m_bytes_remaining); + // We want to use the entire packet since some codecs will require extra information for decoding + const int bytes_decoded = avcodec_decode_video2(m_context, m_frame.get(), &frame_finished, &(packet.packet)); if (bytes_decoded < 0) throw std::runtime_error("avcodec_decode_video failed()"); @@ -191,29 +171,37 @@ void FFmpegDecoderVideo::decodeLoop() m_bytes_remaining -= bytes_decoded; m_packet_data += bytes_decoded; - // Find out the frame pts - - if (packet.packet.dts == int64_t(AV_NOPTS_VALUE) && - m_frame->opaque != 0 && - *reinterpret_cast(m_frame->opaque) != int64_t(AV_NOPTS_VALUE)) - { - pts = *reinterpret_cast(m_frame->opaque); - } - else if (packet.packet.dts != int64_t(AV_NOPTS_VALUE)) - { - pts = packet.packet.dts; - } - else - { - pts = 0; - } - - pts *= av_q2d(m_stream->time_base); - // Publish the frame if we have decoded a complete frame if (frame_finished) { - const double synched_pts = m_clocks.videoSynchClock(m_frame.get(), av_q2d(m_stream->time_base), pts); + AVRational timebase; + // Find out the frame pts + if (m_frame->pts != int64_t(AV_NOPTS_VALUE)) + { + pts = m_frame->pts; + timebase = m_context->time_base; + } + else if (packet.packet.dts == int64_t(AV_NOPTS_VALUE) && + m_frame->opaque != 0 && + *reinterpret_cast(m_frame->opaque) != int64_t(AV_NOPTS_VALUE)) + { + pts = *reinterpret_cast(m_frame->opaque); + timebase = m_stream->time_base; + } + else if (packet.packet.dts != int64_t(AV_NOPTS_VALUE)) + { + pts = packet.packet.dts; + timebase = m_stream->time_base; + } + else + { + pts = 0; + timebase = m_context->time_base; + } + + pts *= av_q2d(timebase); + + const double synched_pts = m_clocks.videoSynchClock(m_frame.get(), av_q2d(timebase), pts); const double frame_delay = m_clocks.videoRefreshSchedule(synched_pts); publishFrame(frame_delay, m_clocks.audioDisabled()); @@ -278,21 +266,21 @@ int FFmpegDecoderVideo::convert(AVPicture *dst, int dst_pix_fmt, AVPicture *src, } - OSG_INFO<<"Using sws_scale "; + OSG_DEBUG<<"Using sws_scale "; int result = sws_scale(m_swscale_ctx, (src->data), (src->linesize), 0, src_height, (dst->data), (dst->linesize)); #else - OSG_INFO<<"Using img_convert "; + OSG_DEBUG<<"Using img_convert "; int result = img_convert(dst, dst_pix_fmt, src, src_pix_fmt, src_width, src_height); #endif osg::Timer_t endTick = osg::Timer::instance()->tick(); - OSG_INFO<<" time = "<delta_m(startTick,endTick)<<"ms"<pix_fmt == PIX_FMT_YUVA420P) yuva420pToRgba(dst, src, width(), height()); else - convert(dst, PIX_FMT_RGB32, src, m_context->pix_fmt, width(), height()); + convert(dst, PIX_FMT_RGB24, src, m_context->pix_fmt, width(), height()); // Wait 'delay' seconds before publishing the picture. int i_delay = static_cast(delay * 1000000 + 0.5); @@ -354,7 +342,7 @@ void FFmpegDecoderVideo::publishFrame(const double delay, bool audio_disabled) void FFmpegDecoderVideo::yuva420pToRgba(AVPicture * const dst, AVPicture * const src, int width, int height) { - convert(dst, PIX_FMT_RGB32, src, m_context->pix_fmt, width, height); + convert(dst, PIX_FMT_RGB24, src, m_context->pix_fmt, width, height); const size_t bpp = 4; diff --git a/src/osgPlugins/ffmpeg/FFmpegHeaders.hpp b/src/osgPlugins/ffmpeg/FFmpegHeaders.hpp index dc79df3ae..96abcabff 100644 --- a/src/osgPlugins/ffmpeg/FFmpegHeaders.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegHeaders.hpp @@ -5,17 +5,20 @@ extern "C" { -#define __STDC_CONSTANT_MACROS #define FF_API_OLD_SAMPLE_FMT 0 #include // for error codes defined in avformat.h #include -#include -#include -#include -#include +#include +#include + +#ifndef ANDROID +#include +#endif + +#include #ifdef USE_SWSCALE - #include + #include #endif #if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(50,38,0) diff --git a/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp b/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp index 26c2ad4d9..592fac8e4 100644 --- a/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegImageStream.cpp @@ -69,7 +69,7 @@ bool FFmpegImageStream::open(const std::string & filename, FFmpegParameters* par return false; setImage( - m_decoder->video_decoder().width(), m_decoder->video_decoder().height(), 1, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE, + m_decoder->video_decoder().width(), m_decoder->video_decoder().height(), 1, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, const_cast(m_decoder->video_decoder().image()), NO_DELETE ); @@ -329,7 +329,7 @@ void FFmpegImageStream::publishNewFrame(const FFmpegDecoderVideo &, void * user_ #if 1 this_->setImage( - this_->m_decoder->video_decoder().width(), this_->m_decoder->video_decoder().height(), 1, GL_RGBA, GL_BGRA, GL_UNSIGNED_BYTE, + this_->m_decoder->video_decoder().width(), this_->m_decoder->video_decoder().height(), 1, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, const_cast(this_->m_decoder->video_decoder().image()), NO_DELETE ); #else diff --git a/src/osgPlugins/ffmpeg/FFmpegParameters.cpp b/src/osgPlugins/ffmpeg/FFmpegParameters.cpp index 57f912024..1cb819f2a 100644 --- a/src/osgPlugins/ffmpeg/FFmpegParameters.cpp +++ b/src/osgPlugins/ffmpeg/FFmpegParameters.cpp @@ -8,25 +8,18 @@ #if LIBAVCODEC_VERSION_MAJOR >= 53 extern "C" { - #include + #include } #define av_parse_video_frame_size av_parse_video_size #define av_parse_video_frame_rate av_parse_video_rate #endif -#if LIBAVCODEC_VERSION_MAJOR >= 53 || \ - (LIBAVCODEC_VERSION_MAJOR==52 && LIBAVCODEC_VERSION_MINOR>=49) +extern "C" +{ + #include +} - extern "C" - { - #include - } - - inline PixelFormat osg_av_get_pix_fmt(const char *name) { return av_get_pix_fmt(name); } - -#else - inline PixelFormat osg_av_get_pix_fmt(const char *name) { return avcodec_get_pix_fmt(name); } -#endif +inline PixelFormat osg_av_get_pix_fmt(const char *name) { return av_get_pix_fmt(name); } namespace osgFFmpeg { @@ -34,14 +27,18 @@ namespace osgFFmpeg { FFmpegParameters::FFmpegParameters() : + m_context(0), + m_options(0), m_format(0) { - memset(&m_parameters, 0, sizeof(m_parameters)); + // Initialize the dictionary + av_dict_set(&m_options, "foo", "bar", 0); } - FFmpegParameters::~FFmpegParameters() -{} +{ + av_dict_free(&m_options); +} void FFmpegParameters::parse(const std::string& name, const std::string& value) @@ -50,50 +47,19 @@ void FFmpegParameters::parse(const std::string& name, const std::string& value) { return; } - else if (name == "format") + if (name == "format") { +#ifndef ANDROID avdevice_register_all(); +#endif m_format = av_find_input_format(value.c_str()); if (!m_format) OSG_NOTICE<<"Failed to apply input video format: "<> audio_sample_rate; - m_parameters.sample_rate = audio_sample_rate; - } + av_dict_set(&m_options, "framerate", value.c_str(), 0); + else + av_dict_set(&m_options, name.c_str(), value.c_str(), 0); } diff --git a/src/osgPlugins/ffmpeg/FFmpegParameters.hpp b/src/osgPlugins/ffmpeg/FFmpegParameters.hpp index 1c2d4b922..ebfc17f6b 100644 --- a/src/osgPlugins/ffmpeg/FFmpegParameters.hpp +++ b/src/osgPlugins/ffmpeg/FFmpegParameters.hpp @@ -21,14 +21,17 @@ public: bool isFormatAvailable() const { return m_format!=NULL; } AVInputFormat* getFormat() { return m_format; } - AVFormatParameters* getFormatParameter() { return &m_parameters; } + AVDictionary** getOptions() { return &m_options; } + void setContext(AVIOContext* context) { m_context = context; } + AVIOContext* getContext() { return m_context; } void parse(const std::string& name, const std::string& value); protected: AVInputFormat* m_format; - AVFormatParameters m_parameters; + AVIOContext* m_context; + AVDictionary* m_options; }; diff --git a/src/osgPlugins/ffmpeg/ReaderWriterFFmpeg.cpp b/src/osgPlugins/ffmpeg/ReaderWriterFFmpeg.cpp index ba20b83b8..081c22aac 100644 --- a/src/osgPlugins/ffmpeg/ReaderWriterFFmpeg.cpp +++ b/src/osgPlugins/ffmpeg/ReaderWriterFFmpeg.cpp @@ -26,7 +26,46 @@ #define USE_AV_LOCK_MANAGER #endif +extern "C" { +static void log_to_osg(void *ptr, int level, const char *fmt, va_list vl) +{ + char logbuf[256]; + vsnprintf(logbuf, sizeof(logbuf), fmt, vl); + logbuf[sizeof(logbuf) - 1] = '\0'; + + osg::NotifySeverity severity = osg::DEBUG_FP; + + switch (level) { + case AV_LOG_PANIC: + severity = osg::ALWAYS; + break; + case AV_LOG_FATAL: + severity = osg::FATAL; + break; + case AV_LOG_ERROR: + severity = osg::WARN; + break; + case AV_LOG_WARNING: + severity = osg::NOTICE; + break; + case AV_LOG_INFO: + severity = osg::INFO; + break; + case AV_LOG_VERBOSE: + severity = osg::DEBUG_INFO; + break; + default: + case AV_LOG_DEBUG: + severity = osg::DEBUG_FP; + break; + } + + // Most av_logs have a trailing newline already + osg::notify(severity) << logbuf; +} + +} // extern "C" /** Implementation heavily inspired by http://www.dranger.com/ffmpeg/ */ @@ -38,6 +77,8 @@ public: { supportsProtocol("http","Read video/audio from http using ffmpeg."); supportsProtocol("rtsp","Read video/audio from rtsp using ffmpeg."); + supportsProtocol("rtp","Read video/audio from rtp using ffmpeg."); + supportsProtocol("tcp","Read video/audio from tcp using ffmpeg."); supportsExtension("ffmpeg", ""); supportsExtension("avi", ""); @@ -61,6 +102,9 @@ public: supportsOption("frame_size", "Set frame size (e.g. 320x240)"); supportsOption("frame_rate", "Set frame rate (e.g. 25)"); supportsOption("audio_sample_rate", "Set audio sampling rate (e.g. 44100)"); + supportsOption("context", "AVIOContext* for custom IO"); + + av_log_set_callback(log_to_osg); #ifdef USE_AV_LOCK_MANAGER // enable thread locking @@ -68,6 +112,8 @@ public: #endif // Register all FFmpeg formats/codecs av_register_all(); + + avformat_network_init(); } virtual ~ReaderWriterFFmpeg() @@ -135,6 +181,14 @@ private: parameters->parse(name, options->getPluginStringData(name)); } } + if (options && options->getNumPluginData()>0) + { + AVIOContext* context = (AVIOContext*)options->getPluginData("context"); + if (context != NULL) + { + parameters->setContext(context); + } + } } #ifdef USE_AV_LOCK_MANAGER