]> git.sesse.net Git - casparcg/commitdiff
Implemented thumbnail generation for video files. Similar to 2.0, but better.
authorHelge Norberg <helge.norberg@svt.se>
Wed, 18 Nov 2015 18:40:40 +0000 (19:40 +0100)
committerHelge Norberg <helge.norberg@svt.se>
Wed, 18 Nov 2015 18:40:40 +0000 (19:40 +0100)
13 files changed:
modules/ffmpeg/consumer/ffmpeg_consumer.cpp
modules/ffmpeg/ffmpeg.cpp
modules/ffmpeg/producer/audio/audio_decoder.cpp
modules/ffmpeg/producer/ffmpeg_producer.cpp
modules/ffmpeg/producer/ffmpeg_producer.h
modules/ffmpeg/producer/input/input.cpp
modules/ffmpeg/producer/input/input.h
modules/ffmpeg/producer/tbb_avcodec.cpp
modules/ffmpeg/producer/tbb_avcodec.h
modules/ffmpeg/producer/util/util.cpp
modules/ffmpeg/producer/util/util.h
modules/ffmpeg/producer/video/video_decoder.cpp
modules/ffmpeg/producer/video/video_decoder.h

index 716a0843e5ee33ccaf67663a1ace0d1219da132d..97a1457a9808f438c92997798e7c6f1ac76dfbbd 100644 (file)
@@ -484,7 +484,7 @@ private:
                if(output_format_.format->flags & AVFMT_GLOBALHEADER)
                        c->flags |= CODEC_FLAG_GLOBAL_HEADER;
                
-               THROW_ON_ERROR2(tbb_avcodec_open(c, encoder), "[ffmpeg_consumer]");
+               THROW_ON_ERROR2(tbb_avcodec_open(c, encoder, false), "[ffmpeg_consumer]");
 
                return std::shared_ptr<AVStream>(st, [](AVStream* st)
                {
index 455e95b1a0ee3f0d9a00d0d3697a4cc801fe6ed3..ff7fa5419e60397a565c225aad23262d0540ba5d 100644 (file)
@@ -295,7 +295,8 @@ void init(core::module_dependencies dependencies)
        dependencies.consumer_registry->register_preconfigured_consumer_factory(L"file", create_preconfigured_consumer);
        dependencies.consumer_registry->register_preconfigured_consumer_factory(L"stream", create_preconfigured_streaming_consumer);
        dependencies.producer_registry->register_producer_factory(L"FFmpeg Producer", create_producer, describe_producer);
-       
+       dependencies.producer_registry->register_thumbnail_producer_factory(create_thumbnail_producer);
+
        dependencies.media_info_repo->register_extractor(
                        [](const std::wstring& file, const std::wstring& extension, core::media_info& info) -> bool
                        {
@@ -306,7 +307,7 @@ void init(core::module_dependencies dependencies)
                                        return true;
                                }
 
-                               if (!is_valid_file(file))
+                               if (!is_valid_file(file, true))
                                        return false;
 
                                info.clip_type = L"MOVIE";
index 27f17e0005bf1487a9c5f67b0c08f76e88dc6989..f94a39c35efed8f068b7760afd81b19921b5123f 100644 (file)
@@ -63,7 +63,7 @@ struct audio_decoder::impl : boost::noncopyable
        input&                                                                                                          input_;
        int                                                                                                                     index_;
        const core::video_format_desc                                                           format_desc_;
-       const spl::shared_ptr<AVCodecContext>                                           codec_context_          = open_codec(input_.context(), AVMEDIA_TYPE_AUDIO, index_);
+       const spl::shared_ptr<AVCodecContext>                                           codec_context_          = open_codec(input_.context(), AVMEDIA_TYPE_AUDIO, index_, false);
 
        std::shared_ptr<SwrContext>                                                                     swr_                            {
                                                                                                                                                                                swr_alloc_set_opts(
index 558e839f1f2ef884b684a51c9f536aef304b9493..7370a5b495c1f6f5a739c11d66e825a70888bcc4 100644 (file)
@@ -106,6 +106,7 @@ struct ffmpeg_producer : public core::frame_producer_base
 
        const double                                                                    fps_                                    = read_fps(input_.context(), format_desc_.fps);
        const uint32_t                                                                  start_;
+       const bool                                                                              thumbnail_mode_;
                
        std::unique_ptr<video_decoder>                                  video_decoder_;
        std::unique_ptr<audio_decoder>                                  audio_decoder_; 
@@ -125,13 +126,15 @@ public:
                        const std::wstring& filter,
                        bool loop,
                        uint32_t start,
-                       uint32_t length)
+                       uint32_t length,
+                       bool thumbnail_mode)
                : filename_(filename)
                , frame_factory_(frame_factory)         
                , format_desc_(format_desc)
-               , input_(graph_, filename_, loop, start, length)
+               , input_(graph_, filename_, loop, start, length, thumbnail_mode)
                , fps_(read_fps(input_.context(), format_desc_.fps))
                , start_(start)
+               , thumbnail_mode_(thumbnail_mode)
        {
                graph_->set_color("frame-time", diagnostics::color(0.1f, 1.0f, 0.1f));
                graph_->set_color("underflow", diagnostics::color(0.6f, 0.3f, 0.9f));   
@@ -140,7 +143,7 @@ public:
 
                try
                {
-                       video_decoder_.reset(new video_decoder(input_));
+                       video_decoder_.reset(new video_decoder(input_, thumbnail_mode));
                        video_decoder_->monitor_output().attach_parent(monitor_subject_);
                        constraints_.width.set(video_decoder_->width());
                        constraints_.height.set(video_decoder_->height());
@@ -162,23 +165,26 @@ public:
 
                auto channel_layout = core::audio_channel_layout::invalid();
 
-               try
+               if (!thumbnail_mode)
                {
-                       audio_decoder_ .reset(new audio_decoder(input_, format_desc_, channel_layout_spec));
-                       audio_decoder_->monitor_output().attach_parent(monitor_subject_);
+                       try
+                       {
+                               audio_decoder_.reset(new audio_decoder(input_, format_desc_, channel_layout_spec));
+                               audio_decoder_->monitor_output().attach_parent(monitor_subject_);
 
-                       channel_layout = audio_decoder_->channel_layout();
-                       
-                       CASPAR_LOG(info) << print() << L" " << audio_decoder_->print();
-               }
-               catch(averror_stream_not_found&)
-               {
-                       //CASPAR_LOG(warning) << print() << " No audio-stream found. Running without audio.";   
-               }
-               catch(...)
-               {
-                       CASPAR_LOG_CURRENT_EXCEPTION();
-                       CASPAR_LOG(warning) << print() << " Failed to open audio-stream. Running without audio.";               
+                               channel_layout = audio_decoder_->channel_layout();
+
+                               CASPAR_LOG(info) << print() << L" " << audio_decoder_->print();
+                       }
+                       catch (averror_stream_not_found&)
+                       {
+                               CASPAR_LOG(debug) << print() << " No audio-stream found. Running without audio.";       
+                       }
+                       catch (...)
+                       {
+                               CASPAR_LOG_CURRENT_EXCEPTION();
+                               CASPAR_LOG(warning) << print() << " Failed to open audio-stream. Running without audio.";
+                       }
                }
 
                if (start_ > file_nb_frames())
@@ -227,6 +233,74 @@ public:
                return frame;
        }
 
+       core::draw_frame render_specific_frame(uint32_t file_position)
+       {
+               muxer_->clear();
+               input_.seek(file_position);
+
+               decode_next_frame();
+
+               if (muxer_->empty())
+               {
+                       CASPAR_LOG(trace) << print() << " Giving up finding frame at " << file_position;
+
+                       return core::draw_frame::empty();
+               }
+
+               auto frame = muxer_->front();
+               muxer_->pop();
+
+               return frame;
+       }
+
+       core::draw_frame create_thumbnail_frame() override
+       {
+               auto quiet_logging = temporary_enable_quiet_logging_for_thread(true);
+
+               auto total_frames = nb_frames();
+               auto grid = env::properties().get(L"configuration.thumbnails.video-grid", 2);
+
+               if (grid < 1)
+               {
+                       CASPAR_LOG(error) << L"configuration/thumbnails/video-grid cannot be less than 1";
+                       BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("configuration/thumbnails/video-grid cannot be less than 1"));
+               }
+
+               if (grid == 1)
+               {
+                       return render_specific_frame(total_frames / 2);
+               }
+
+               auto num_snapshots = grid * grid;
+
+               std::vector<core::draw_frame> frames;
+
+               for (int i = 0; i < num_snapshots; ++i)
+               {
+                       int x = i % grid;
+                       int y = i / grid;
+                       int desired_frame;
+
+                       if (i == 0)
+                               desired_frame = 0; // first
+                       else if (i == num_snapshots - 1)
+                               desired_frame = total_frames - 1; // last
+                       else
+                               // evenly distributed across the file.
+                               desired_frame = total_frames * i / (num_snapshots - 1);
+
+                       auto frame = render_specific_frame(desired_frame);
+                       frame.transform().image_transform.fill_scale[0] = 1.0 / static_cast<double>(grid);
+                       frame.transform().image_transform.fill_scale[1] = 1.0 / static_cast<double>(grid);
+                       frame.transform().image_transform.fill_translation[0] = 1.0 / static_cast<double>(grid) * x;
+                       frame.transform().image_transform.fill_translation[1] = 1.0 / static_cast<double>(grid) * y;
+
+                       frames.push_back(frame);
+               }
+
+               return core::draw_frame(frames);
+       }
+
        core::draw_frame last_frame() override
        {
                end_seek();
@@ -455,7 +529,7 @@ void describe_producer(core::help_sink& sink, const core::help_repository& repo)
 
 spl::shared_ptr<core::frame_producer> create_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params)
 {              
-       auto filename = probe_stem(env::media_folder() + L"/" + params.at(0));
+       auto filename = probe_stem(env::media_folder() + L"/" + params.at(0), false);
 
        if(filename.empty())
                return core::frame_producer::empty();
@@ -465,6 +539,7 @@ spl::shared_ptr<core::frame_producer> create_producer(const core::frame_producer
        auto length                     = get_param(L"LENGTH", params, std::numeric_limits<uint32_t>::max());
        auto filter_str         = get_param(L"FILTER", params, L"");
        auto channel_layout     = get_param(L"CHANNEL_LAYOUT", params, L"");
+       bool thumbnail_mode     = false;
 
        return create_destroy_proxy(spl::make_shared_ptr(std::make_shared<ffmpeg_producer>(
                        dependencies.frame_factory,
@@ -474,7 +549,35 @@ spl::shared_ptr<core::frame_producer> create_producer(const core::frame_producer
                        filter_str,
                        loop,
                        start,
-                       length)));
+                       length,
+                       thumbnail_mode)));
+}
+
+spl::shared_ptr<core::frame_producer> create_thumbnail_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params)
+{
+       auto quiet_logging = temporary_enable_quiet_logging_for_thread(true);
+       auto filename = probe_stem(env::media_folder() + L"/" + params.at(0), true);
+
+       if(filename.empty())
+               return core::frame_producer::empty();
+       
+       bool loop                       = false;
+       auto start                      = 0;
+       auto length                     = std::numeric_limits<uint32_t>::max();
+       auto filter_str         = L"";
+       auto channel_layout     = L"";
+       bool thumbnail_mode     = true;
+
+       return spl::make_shared_ptr(std::make_shared<ffmpeg_producer>(
+                       dependencies.frame_factory,
+                       dependencies.format_desc,
+                       channel_layout,
+                       filename,
+                       filter_str,
+                       loop,
+                       start,
+                       length,
+                       thumbnail_mode));
 }
 
 }}
index 4359b2c90504d60b5f7b6771d1ee378fb7ac43ad..77580085abdaed149e90144c5c640cac8cce815e 100644 (file)
@@ -32,5 +32,6 @@ namespace caspar { namespace ffmpeg {
 
 void describe_producer(core::help_sink& sink, const core::help_repository& repo);
 spl::shared_ptr<core::frame_producer> create_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params);
+spl::shared_ptr<core::frame_producer> create_thumbnail_producer(const core::frame_producer_dependencies& dependencies, const std::vector<std::wstring>& params);
 
 }}
\ No newline at end of file
index 6b982f84d57b96cc9d526da49778f19956c2c58e..64a75f6758cfa7ea19d2584742ec6f38e6d83b34 100644 (file)
@@ -120,11 +120,12 @@ struct input::impl : boost::noncopyable
        tbb::atomic<uint32_t>                                           length_;
        tbb::atomic<bool>                                                       loop_;
        tbb::atomic<bool>                                                       eof_;
+       bool                                                                            thumbnail_mode_;
        double                                                                          fps_                                    = read_fps(*format_context_, 0.0);
        uint32_t                                                                        frame_number_                   = 0;
 
-       stream                                                                          video_stream_                   { av_find_best_stream(format_context_.get(), AVMEDIA_TYPE_VIDEO, -1, -1, 0, 0) };
-       stream                                                                          audio_stream_                   { av_find_best_stream(format_context_.get(), AVMEDIA_TYPE_AUDIO, -1, -1, 0, 0) };
+       stream                                                                          video_stream_                   {                                                       av_find_best_stream(format_context_.get(), AVMEDIA_TYPE_VIDEO, -1, -1, 0, 0) };
+       stream                                                                          audio_stream_                   { thumbnail_mode_ ? -1 :        av_find_best_stream(format_context_.get(), AVMEDIA_TYPE_AUDIO, -1, -1, 0, 0) };
 
        boost::optional<uint32_t>                                       seek_target_;
 
@@ -133,10 +134,17 @@ struct input::impl : boost::noncopyable
        boost::condition_variable                                       cond_;
        boost::thread                                                           thread_;
        
-       impl(const spl::shared_ptr<diagnostics::graph> graph, const std::wstring& filename, const bool loop, const uint32_t start, const uint32_t length) 
+       impl(
+                       const spl::shared_ptr<diagnostics::graph> graph,
+                       const std::wstring& filename,
+                       const bool loop,
+                       const uint32_t start,
+                       const uint32_t length,
+                       bool thumbnail_mode)
                : graph_(graph)
                , filename_(filename)
-       {               
+               , thumbnail_mode_(thumbnail_mode)
+       {
                start_                  = start;
                length_                 = length;
                loop_                   = loop;
@@ -157,14 +165,17 @@ struct input::impl : boost::noncopyable
                for(int n = 0; n < 8; ++n)
                        tick();
 
-               thread_ = boost::thread([this]{run();});
+               if (!thumbnail_mode)
+                       thread_ = boost::thread([this]{run();});
        }
 
        ~impl()
        {
                is_running_ = false;
                cond_.notify_one();
-               thread_.join();
+
+               if (!thumbnail_mode_)
+                       thread_.join();
        }
        
        bool try_pop_video(std::shared_ptr<AVPacket>& packet)
@@ -172,7 +183,24 @@ struct input::impl : boost::noncopyable
                if (!video_stream_.is_available())
                        return false;
 
+               if (thumbnail_mode_)
+               {
+                       int ticks = 0;
+                       while (!video_stream_.try_pop(packet))
+                       {
+                               tick();
+                               if (++ticks > 32) // Infinite loop should not be possible
+                                       return false;
+
+                               // Play nice
+                               boost::this_thread::sleep(boost::posix_time::milliseconds(5));
+                       }
+
+                       return true;
+               }
+
                bool result = video_stream_.try_pop(packet);
+
                if(result)
                        cond_.notify_one();
                
@@ -204,7 +232,7 @@ struct input::impl : boost::noncopyable
                        video_stream_.clear();
                        audio_stream_.clear();
                }
-               
+
                cond_.notify_one();
        }
                
@@ -237,14 +265,15 @@ private:
                        }
                }
                
-               auto stream     = format_context_->streams[default_stream_index_];
-               auto fps        = read_fps(*format_context_, 0.0);
+               auto stream                             = format_context_->streams[default_stream_index_];
+               auto fps                                = read_fps(*format_context_, 0.0);
+               auto target_timestamp = static_cast<int64_t>((target / fps * stream->time_base.den) / stream->time_base.num);
                
                THROW_ON_ERROR2(avformat_seek_file(
                                format_context_.get(),
                                default_stream_index_,
                                std::numeric_limits<int64_t>::min(),
-                               static_cast<int64_t>((target / fps * stream->time_base.den) / stream->time_base.num),
+                               target_timestamp,
                                std::numeric_limits<int64_t>::max(),
                                0), print());
                
@@ -347,8 +376,8 @@ private:
        }
 };
 
-input::input(const spl::shared_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, uint32_t start, uint32_t length
-       : impl_(new impl(graph, filename, loop, start, length)){}
+input::input(const spl::shared_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, uint32_t start, uint32_t length, bool thumbnail_mode)
+       : impl_(new impl(graph, filename, loop, start, length, thumbnail_mode)){}
 bool input::try_pop_video(std::shared_ptr<AVPacket>& packet){return impl_->try_pop_video(packet);}
 bool input::try_pop_audio(std::shared_ptr<AVPacket>& packet){return impl_->try_pop_audio(packet);}
 AVFormatContext& input::context(){return *impl_->format_context_;}
index 22a02ee46364fe339d7d36b2e99b56f356d3ffe1..a4a7dbb23a732526a976e34b1b5b7e2094dc0067 100644 (file)
@@ -45,7 +45,12 @@ namespace ffmpeg {
 class input : boost::noncopyable
 {
 public:
-       explicit input(const spl::shared_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, uint32_t start, uint32_t length);
+       explicit input(
+                       const spl::shared_ptr<diagnostics::graph>& graph,
+                       const std::wstring& filename,
+                       bool loop, uint32_t start,
+                       uint32_t length,
+                       bool thumbnail_mode);
 
        bool            try_pop_video(std::shared_ptr<AVPacket>& packet);
        bool            try_pop_audio(std::shared_ptr<AVPacket>& packet);
index a371c332dcce902b2849a6e8af4bbce24416510f..ac412b462fb6ad992e142132c85c7f0acc64b91e 100644 (file)
@@ -103,14 +103,14 @@ void thread_free(AVCodecContext* s)
        s->thread_opaque = nullptr;
 }
 
-int tbb_avcodec_open(AVCodecContext* avctx, AVCodec* codec)
+int tbb_avcodec_open(AVCodecContext* avctx, AVCodec* codec, bool single_threaded)
 {
        if(codec->capabilities & CODEC_CAP_EXPERIMENTAL)
                CASPAR_THROW_EXCEPTION(invalid_argument() << msg_info("Experimental codecs are not supported."));
 
        avctx->thread_count = 1;
 
-       if(codec->capabilities & CODEC_CAP_SLICE_THREADS)       
+       if(!single_threaded && codec->capabilities & CODEC_CAP_SLICE_THREADS)
                thread_init(avctx);
        
        // ff_thread_init will not be executed since thread_opaque != nullptr || thread_count == 1.
index de9b6baf5d98f6ad39035784ee1ac724373b4e72..25020e612297d1e70c9cb373a8d416a80223fd24 100644 (file)
@@ -26,7 +26,7 @@ struct AVCodec;
 
 namespace caspar {
        
-int tbb_avcodec_open(AVCodecContext *avctx, AVCodec *codec);
+int tbb_avcodec_open(AVCodecContext *avctx, AVCodec *codec, bool single_threaded);
 int tbb_avcodec_close(AVCodecContext *avctx);
 
 }
\ No newline at end of file
index 865b96bcb3f19c950c6d2f5c3063b945d587d502..c5fdd312f16647605e1ec799550cb90c3aa902fd 100644 (file)
@@ -476,14 +476,14 @@ spl::shared_ptr<AVFrame> create_frame()
        return frame;
 }
 
-spl::shared_ptr<AVCodecContext> open_codec(AVFormatContext& context, enum AVMediaType type, int& index)
+spl::shared_ptr<AVCodecContext> open_codec(AVFormatContext& context, enum AVMediaType type, int& index, bool single_threaded)
 {      
        AVCodec* decoder;
        index = THROW_ON_ERROR2(av_find_best_stream(&context, type, -1, -1, &decoder, 0), "");
        //if(strcmp(decoder->name, "prores") == 0 && decoder->next && strcmp(decoder->next->name, "prores_lgpl") == 0)
        //      decoder = decoder->next;
 
-       THROW_ON_ERROR2(tbb_avcodec_open(context.streams[index]->codec, decoder), "");
+       THROW_ON_ERROR2(tbb_avcodec_open(context.streams[index]->codec, decoder, single_threaded), "");
        return spl::shared_ptr<AVCodecContext>(context.streams[index]->codec, tbb_avcodec_close);
 }
 
@@ -508,7 +508,7 @@ std::wstring print_mode(int width, int height, double fps, bool interlaced)
        return boost::lexical_cast<std::wstring>(width) + L"x" + boost::lexical_cast<std::wstring>(height) + (!interlaced ? L"p" : L"i") + fps_ss.str();
 }
 
-bool is_valid_file(const std::wstring& filename)
+bool is_valid_file(const std::wstring& filename, bool only_video)
 {                              
        static const auto invalid_exts = {
                L".png",
@@ -526,6 +526,11 @@ bool is_valid_file(const std::wstring& filename)
                L".swf",
                L".ct"
        };
+       static const auto only_audio = {
+               L".mp3",
+               L".wav",
+               L".wma"
+       };
        static const auto valid_exts = {
                L".m2t",
                L".mov",
@@ -533,8 +538,6 @@ bool is_valid_file(const std::wstring& filename)
                L".dv",
                L".flv",
                L".mpg",
-               L".wav",
-               L".mp3",
                L".dnxhd",
                L".h264",
                L".prores"
@@ -543,11 +546,17 @@ bool is_valid_file(const std::wstring& filename)
        auto ext = boost::to_lower_copy(boost::filesystem::path(filename).extension().wstring());
                
        if(std::find(valid_exts.begin(), valid_exts.end(), ext) != valid_exts.end())
-               return true;    
+               return true;
+
+       if (!only_video && std::find(only_audio.begin(), only_audio.end(), ext) != only_audio.end())
+               return true;
        
        if(std::find(invalid_exts.begin(), invalid_exts.end(), ext) != invalid_exts.end())
                return false;   
 
+       if (only_video && std::find(only_audio.begin(), only_audio.end(), ext) != only_audio.end())
+               return false;
+
        auto u8filename = u8(filename);
        
        int score = 0;
@@ -603,7 +612,7 @@ bool try_get_duration(const std::wstring filename, std::int64_t& duration, boost
        return true;
 }
 
-std::wstring probe_stem(const std::wstring& stem)
+std::wstring probe_stem(const std::wstring& stem, bool only_video)
 {
        auto stem2 = boost::filesystem::path(stem);
        auto parent = find_case_insensitive(stem2.parent_path().wstring());
@@ -615,7 +624,7 @@ std::wstring probe_stem(const std::wstring& stem)
 
        for(auto it = boost::filesystem::directory_iterator(dir); it != boost::filesystem::directory_iterator(); ++it)
        {
-               if(boost::iequals(it->path().stem().wstring(), stem2.filename().wstring()) && is_valid_file(it->path().wstring()))
+               if(boost::iequals(it->path().stem().wstring(), stem2.filename().wstring()) && is_valid_file(it->path().wstring(), only_video))
                        return it->path().wstring();
        }
        return L"";
index 643aa273a1684d212af2f8c39aaeca67afe6d7b2..41e7d4e5fca9ff507242719491261231f621c21e 100644 (file)
@@ -58,7 +58,6 @@ namespace caspar { namespace ffmpeg {
 core::field_mode                                       get_mode(const AVFrame& frame);
 core::mutable_frame                                    make_frame(const void* tag, const spl::shared_ptr<AVFrame>& decoded_frame, double fps, core::frame_factory& frame_factory, const core::audio_channel_layout& channel_layout);
 spl::shared_ptr<AVFrame>                       make_av_frame(core::mutable_frame& frame);
-spl::shared_ptr<AVFrame>                       make_av_frame(core::const_frame& frame);
 spl::shared_ptr<AVFrame>                       make_av_frame(std::array<uint8_t*, 4> data, const core::pixel_format_desc& pix_desc);
 
 core::pixel_format_desc                                pixel_format_desc(PixelFormat pix_fmt, int width, int height);
@@ -66,7 +65,7 @@ core::pixel_format_desc                               pixel_format_desc(PixelFormat pix_fmt, int width, int
 spl::shared_ptr<AVPacket> create_packet();
 spl::shared_ptr<AVFrame>  create_frame();
 
-spl::shared_ptr<AVCodecContext> open_codec(AVFormatContext& context, AVMediaType type, int& index);
+spl::shared_ptr<AVCodecContext> open_codec(AVFormatContext& context, AVMediaType type, int& index, bool single_threaded);
 spl::shared_ptr<AVFormatContext> open_input(const std::wstring& filename);
 
 bool is_sane_fps(AVRational time_base);
@@ -76,8 +75,8 @@ double read_fps(AVFormatContext& context, double fail_value);
 
 std::wstring print_mode(int width, int height, double fps, bool interlaced);
 
-std::wstring probe_stem(const std::wstring& stem);
-bool is_valid_file(const std::wstring& filename);
+std::wstring probe_stem(const std::wstring& stem, bool only_video);
+bool is_valid_file(const std::wstring& filename, bool only_video);
 bool try_get_duration(const std::wstring filename, std::int64_t& duration, boost::rational<std::int64_t>& time_base);
 
 core::audio_channel_layout get_audio_channel_layout(const AVCodecContext& codec_context, const std::wstring& channel_layout_spec);
index d1ff22afcb2130c8261bddada5dcc9f2bc991950..2778a5cebe08b9646d766a52263280f55c9884b4 100644 (file)
@@ -72,9 +72,9 @@ struct video_decoder::impl : boost::noncopyable
        std::shared_ptr<AVPacket>                               current_packet_;
 
 public:
-       explicit impl(input& in
+       explicit impl(input& in, bool single_threaded)
                : input_(&in)
-               , codec_context_(open_codec(input_->context(), AVMEDIA_TYPE_VIDEO, index_))
+               , codec_context_(open_codec(input_->context(), AVMEDIA_TYPE_VIDEO, index_, single_threaded))
                , stream_(input_->context().streams[index_])
                , nb_frames_(static_cast<uint32_t>(stream_->nb_frames))
                , width_(codec_context_->width)
@@ -162,7 +162,7 @@ public:
        }
 };
 
-video_decoder::video_decoder(input& in) : impl_(new impl(in)){}
+video_decoder::video_decoder(input& in, bool single_threaded) : impl_(new impl(in, single_threaded)){}
 video_decoder::video_decoder(video_decoder&& other) : impl_(std::move(other.impl_)){}
 video_decoder& video_decoder::operator=(video_decoder&& other){impl_ = std::move(other.impl_); return *this;}
 std::shared_ptr<AVFrame> video_decoder::operator()(){return impl_->poll();}
index f911aa0246e5939beeea7b411bab7c1ec2997c6c..f5768f675702be6bfe0a76a25159db2be8a6df76 100644 (file)
@@ -37,7 +37,7 @@ namespace caspar { namespace ffmpeg {
 class video_decoder : public boost::noncopyable
 {
 public:
-       explicit video_decoder(class input& input);
+       explicit video_decoder(class input& input, bool single_threaded);
        
        video_decoder(video_decoder&& other);
        video_decoder& operator=(video_decoder&& other);