]> git.sesse.net Git - casparcg/commitdiff
Merge branch '2.1.0' of https://github.com/CasparCG/Server into 2.1.0
authorHelge Norberg <helge.norberg@svt.se>
Mon, 3 Oct 2016 14:44:09 +0000 (16:44 +0200)
committerHelge Norberg <helge.norberg@svt.se>
Mon, 3 Oct 2016 14:44:09 +0000 (16:44 +0200)
1  2 
modules/ffmpeg/producer/audio/audio_decoder.cpp
modules/ffmpeg/producer/ffmpeg_producer.cpp
modules/ffmpeg/producer/input/input.cpp
modules/ffmpeg/producer/video/video_decoder.cpp

index 6ed608a88d1fea57468ffc4f95908416331cbb49,fe69a59cf6aa459f8667ebaf2bdf0864d99bb00e..ac7b44e423e91f2913bbd6c20bf4389349d1aab5
@@@ -19,7 -19,7 +19,7 @@@
  * Author: Robert Nagy, ronag89@gmail.com
  */
  
- #include "../../stdafx.h"
+ #include "../../StdAfx.h"
  
  #include "audio_decoder.h"
  
@@@ -37,7 -37,7 +37,7 @@@
  #pragma warning (push)
  #pragma warning (disable : 4244)
  #endif
 -extern "C" 
 +extern "C"
  {
        #include <libavformat/avformat.h>
        #include <libavcodec/avcodec.h>
  #endif
  
  namespace caspar { namespace ffmpeg {
 -      
 +
  struct audio_decoder::implementation : boost::noncopyable
 -{     
 +{
        int                                                                             index_                          = -1;
        const spl::shared_ptr<AVCodecContext>   codec_context_;
        const int                                                               out_samplerate_;
 -      
 +
        cache_aligned_vector<int32_t>                   buffer_;
  
        std::queue<spl::shared_ptr<AVPacket>>   packets_;
@@@ -85,29 -85,29 +85,29 @@@ public
                : codec_context_(open_codec(*context, AVMEDIA_TYPE_AUDIO, index_, false))
                , out_samplerate_(out_samplerate)
                , buffer_(10 * out_samplerate_ * codec_context_->channels) // 10 seconds of audio
 -      {       
 +      {
                if(!swr_)
 -                      BOOST_THROW_EXCEPTION(bad_alloc());
 -              
 +                      CASPAR_THROW_EXCEPTION(bad_alloc());
 +
                THROW_ON_ERROR2(swr_init(swr_.get()), "[audio_decoder]");
  
                codec_context_->refcounted_frames = 1;
        }
  
        void push(const std::shared_ptr<AVPacket>& packet)
 -      {                       
 +      {
                if(!packet)
                        return;
  
                if(packet->stream_index == index_ || packet->data == nullptr)
                        packets_.push(spl::make_shared_ptr(packet));
 -      }       
 -      
 +      }
 +
        std::shared_ptr<core::mutable_audio_buffer> poll()
        {
                if(packets_.empty())
                        return nullptr;
 -                              
 +
                auto packet = packets_.front();
  
                if(packet->data == nullptr)
  
                auto audio = decode(*packet);
  
 -              if(packet->size == 0)                                   
 +              if(packet->size == 0)
                        packets_.pop();
  
                return audio;
        }
  
        std::wstring print() const
 -      {               
 +      {
                return L"[audio-decoder] " + u16(codec_context_->codec->long_name);
        }
  };
index 5f2b995eba61bd22458c23f0e634ada24cd43016,a77994896eabfd92baf5fa8acaba7da712e6987b..4a4ca8a1b0c9c81ebefcc067cab09675317cef4d
@@@ -79,6 -79,8 +79,6 @@@ struct ffmpeg_producer : public core::f
        const std::wstring                                                                      filename_;
        const std::wstring                                                                      path_relative_to_media_         = get_relative_or_original(filename_, env::media_folder());
  
 -      FFMPEG_Resource                                                                         resource_type_;
 -
        const spl::shared_ptr<diagnostics::graph>                       graph_;
        timer                                                                                           frame_timer_;
  
@@@ -108,7 -110,8 +108,7 @@@ public
        explicit ffmpeg_producer(
                        const spl::shared_ptr<core::frame_factory>& frame_factory,
                        const core::video_format_desc& format_desc,
 -                      const std::wstring& filename,
 -                      FFMPEG_Resource resource_type,
 +                      const std::wstring& url_or_file,
                        const std::wstring& filter,
                        bool loop,
                        uint32_t start,
                        bool thumbnail_mode,
                        const std::wstring& custom_channel_order,
                        const ffmpeg_options& vid_params)
 -              : filename_(filename)
 -              , resource_type_(resource_type)
 +              : filename_(url_or_file)
                , frame_factory_(frame_factory)
                , initial_logger_disabler_(temporary_enable_quiet_logging_for_thread(thumbnail_mode))
 -              , input_(graph_, filename_, resource_type, loop, start, length, thumbnail_mode, vid_params)
 +              , input_(graph_, url_or_file, loop, start, length, thumbnail_mode, vid_params)
                , framerate_(read_framerate(*input_.context(), format_desc.framerate))
                , start_(start)
                , length_(length)
                                send_osc();
                                return std::make_pair(last_frame(), -1);
                        }
 -                      else if (resource_type_ == FFMPEG_Resource::FFMPEG_FILE)
 +                      else if (!is_url())
                        {
                                graph_->set_tag(diagnostics::tag_severity::WARNING, "underflow");
                                send_osc();
 -                              return std::make_pair(core::draw_frame::late(), -1);
 +                              return std::make_pair(last_frame_, -1);
                        }
                        else
                        {
                                send_osc();
 -                              return std::make_pair(last_frame(), -1);
 +                              return std::make_pair(last_frame_, -1);
                        }
                }
  
                return frame;
        }
  
 +      bool is_url() const
 +      {
 +              return boost::contains(filename_, L"://");
 +      }
 +
        void send_osc()
        {
                double fps = static_cast<double>(framerate_.numerator()) / static_cast<double>(framerate_.denominator());
                                           // therefore no seeking should be necessary for the first frame.
                {
                        input_.seek(file_position > 1 ? file_position - 2: file_position).get();
-                       boost::this_thread::sleep(boost::posix_time::milliseconds(40));
+             boost::this_thread::sleep_for(boost::chrono::milliseconds(40));
                }
  
                for (int i = 0; i < NUM_RETRIES; ++i)
                {
-                       boost::this_thread::sleep(boost::posix_time::milliseconds(40));
+             boost::this_thread::sleep_for(boost::chrono::milliseconds(40));
  
                        auto frame = render_frame();
  
                                {
                                        CASPAR_LOG(trace) << print() << L" adjusting to " << adjusted_seek;
                                        input_.seek(static_cast<uint32_t>(adjusted_seek) - 1).get();
-                                       boost::this_thread::sleep(boost::posix_time::milliseconds(40));
+                     boost::this_thread::sleep_for(boost::chrono::milliseconds(40));
                                }
                                else
                                        return frame.first;
                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"));
 +                      CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info("configuration/thumbnails/video-grid cannot be less than 1"));
                }
  
                if (grid == 1)
  
        uint32_t nb_frames() const override
        {
 -              if (resource_type_ == FFMPEG_Resource::FFMPEG_DEVICE || resource_type_ == FFMPEG_Resource::FFMPEG_STREAM || input_.loop())
 +              if (is_url() || input_.loop())
                        return std::numeric_limits<uint32_t>::max();
  
                uint32_t nb_frames = file_nb_frames();
  
        std::wstring print() const override
        {
 -              return L"ffmpeg[" + boost::filesystem::path(filename_).filename().wstring() + L"|"
 +              return L"ffmpeg[" + (is_url() ? filename_ : boost::filesystem::path(filename_).filename().wstring()) + L"|"
                                                  + print_mode() + L"|"
                                                  + boost::lexical_cast<std::wstring>(file_frame_number_) + L"/" + boost::lexical_cast<std::wstring>(file_nb_frames()) + L"]";
        }
  void describe_producer(core::help_sink& sink, const core::help_repository& repo)
  {
        sink.short_description(L"A producer for playing media files supported by FFmpeg.");
 -      sink.syntax(L"[clip:string] {[loop:LOOP]} {SEEK [start:int]} {LENGTH [start:int]} {FILTER [filter:string]} {CHANNEL_LAYOUT [channel_layout:string]}");
 +      sink.syntax(L"[clip,url:string] {[loop:LOOP]} {SEEK [start:int]} {LENGTH [start:int]} {FILTER [filter:string]} {CHANNEL_LAYOUT [channel_layout:string]}");
        sink.para()
                ->text(L"The FFmpeg Producer can play all media that FFmpeg can play, which includes many ")
                ->text(L"QuickTime video codec such as Animation, PNG, PhotoJPEG, MotionJPEG, as well as ")
                ->text(L"H.264, FLV, WMV and several audio codecs as well as uncompressed audio.");
        sink.definitions()
                ->item(L"clip", L"The file without the file extension to play. It should reside under the media folder.")
 +              ->item(L"url", L"If clip contains :// it is instead treated as the URL parameter. The URL can either be any streaming protocol supported by FFmpeg or dshow://video={webcam_name}.")
                ->item(L"loop", L"Will cause the media file to loop between start and start + length")
                ->item(L"start", L"Optionally sets the start frame. 0 by default. If loop is specified this will be the frame where it starts over again.")
                ->item(L"length", L"Optionally sets the length of the clip. If not specified the clip will be played to the end. If loop is specified the file will jump to start position once this number of frames has been played.")
        sink.example(L">> PLAY 1-10 folder/clip FILTER yadif=1,-1", L"to deinterlace the video.");
        sink.example(L">> PLAY 1-10 folder/clip CHANNEL_LAYOUT film", L"given the defaults in casparcg.config this will specifies that the clip has 6 audio channels of the type 5.1 and that they are in the order FL FC FR BL BR LFE regardless of what ffmpeg says.");
        sink.example(L">> PLAY 1-10 folder/clip CHANNEL_LAYOUT \"5.1:LFE FL FC FR BL BR\"", L"specifies that the clip has 6 audio channels of the type 5.1 and that they are in the specified order regardless of what ffmpeg says.");
 +      sink.example(L">> PLAY 1-10 rtmp://example.com/live/stream", L"to play an RTMP stream.");
 +      sink.example(L">> PLAY 1-10 \"dshow://video=Live! Cam Chat HD VF0790\"", L"to use a web camera as video input.");
        sink.para()->text(L"The FFmpeg producer also supports changing some of the settings via ")->code(L"CALL")->text(L":");
        sink.example(L">> CALL 1-10 LOOP 1");
        sink.example(L">> CALL 1-10 START 10");
@@@ -586,15 -582,34 +586,15 @@@ spl::shared_ptr<core::frame_producer> c
                const std::vector<std::wstring>& params,
                const spl::shared_ptr<core::media_info_repository>& info_repo)
  {
 -      // Infer the resource type from the resource_name
 -      auto resource_type      = FFMPEG_Resource::FFMPEG_FILE;
 -      auto tokens                     = protocol_split(params.at(0));
 -      auto filename           = params.at(0);
 +      auto file_or_url        = params.at(0);
  
 -      if (!tokens[0].empty())
 -      {
 -              if (tokens[0] == L"dshow")
 -              {
 -                      // Camera
 -                      resource_type   = FFMPEG_Resource::FFMPEG_DEVICE;
 -                      filename                = tokens[1];
 -              }
 -              else
 -              {
 -                      // Stream
 -                      resource_type   = FFMPEG_Resource::FFMPEG_STREAM;
 -                      filename                = params.at(0);
 -              }
 -      }
 -      else
 +      if (!boost::contains(file_or_url, L"://"))
        {
                // File
 -              resource_type   = FFMPEG_Resource::FFMPEG_FILE;
 -              filename                = probe_stem(env::media_folder() + L"/" + params.at(0), false);
 +              file_or_url = probe_stem(env::media_folder() + L"/" + file_or_url, false);
        }
  
 -      if (filename.empty())
 +      if (file_or_url.empty())
                return core::frame_producer::empty();
  
        auto loop                                       = contains_param(L"LOOP",               params);
        auto producer = spl::make_shared<ffmpeg_producer>(
                        dependencies.frame_factory,
                        dependencies.format_desc,
 -                      filename,
 -                      resource_type,
 +                      file_or_url,
                        filter_str,
                        loop,
                        start,
@@@ -671,6 -687,7 +671,6 @@@ core::draw_frame create_thumbnail_frame
                        dependencies.frame_factory,
                        dependencies.format_desc,
                        filename,
 -                      FFMPEG_Resource::FFMPEG_FILE,
                        filter_str,
                        loop,
                        start,
index 9103c7b261e8370ad14bfb60aa53118215d269de,bc1730314715698ac072490a4eea1a07a58e8fe4..72648037d8d9704374ebaacf0e3ee89d3dc3394e
@@@ -19,7 -19,7 +19,7 @@@
  * Author: Robert Nagy, ronag89@gmail.com
  */
  
- #include "../../stdafx.h"
+ #include "../../StdAfx.h"
  
  #include "input.h"
  
@@@ -34,8 -34,6 +34,8 @@@
  #include <common/executor.h>
  #include <common/except.h>
  #include <common/os/general_protection_fault.h>
 +#include <common/param.h>
 +#include <common/scope_exit.h>
  
  #include <tbb/concurrent_queue.h>
  #include <tbb/atomic.h>
@@@ -87,10 -85,10 +87,10 @@@ struct input::implementation : boost::n
  
        executor                                                                                                        executor_;
  
 -      explicit implementation(const spl::shared_ptr<diagnostics::graph> graph, const std::wstring& filename, FFMPEG_Resource resource_type, bool loop, uint32_t start, uint32_t length, bool thumbnail_mode, const ffmpeg_options& vid_params)
 +      explicit implementation(const spl::shared_ptr<diagnostics::graph> graph, const std::wstring& url_or_file, bool loop, uint32_t start, uint32_t length, bool thumbnail_mode, const ffmpeg_options& vid_params)
                : graph_(graph)
 -              , format_context_(open_input(filename, resource_type, vid_params))
 -              , filename_(filename)
 +              , format_context_(open_input(url_or_file, vid_params))
 +              , filename_(url_or_file)
                , thumbnail_mode_(thumbnail_mode)
                , executor_(print())
        {
                });
        }
  
 -      spl::shared_ptr<AVFormatContext> open_input(const std::wstring resource_name, FFMPEG_Resource resource_type, const ffmpeg_options& vid_params)
 +      spl::shared_ptr<AVFormatContext> open_input(const std::wstring& url_or_file, const ffmpeg_options& vid_params)
        {
 -              AVFormatContext* weak_context = nullptr;
 +              AVDictionary* format_options = nullptr;
  
 -              switch (resource_type) {
 -              case FFMPEG_Resource::FFMPEG_FILE:
 -                      THROW_ON_ERROR2(avformat_open_input(&weak_context, u8(resource_name).c_str(), nullptr, nullptr), resource_name);
 -                      break;
 -              case FFMPEG_Resource::FFMPEG_DEVICE:
 -                      {
 -                              AVDictionary* format_options = NULL;
 -                              for (auto& option  : vid_params)
 -                              {
 -                                      av_dict_set(&format_options, option.first.c_str(), option.second.c_str(), 0);
 -                              }
 -                              AVInputFormat* input_format = av_find_input_format("dshow");
 -                              THROW_ON_ERROR2(avformat_open_input(&weak_context, u8(resource_name).c_str(), input_format, &format_options), resource_name);
 -                              if (format_options != nullptr)
 -                              {
 -                                      std::string unsupported_tokens = "";
 -                                      AVDictionaryEntry *t = NULL;
 -                                      while ((t = av_dict_get(format_options, "", t, AV_DICT_IGNORE_SUFFIX)) != nullptr)
 -                                      {
 -                                              if (!unsupported_tokens.empty())
 -                                                      unsupported_tokens += ", ";
 -                                              unsupported_tokens += t->key;
 -                                      }
 -                                      avformat_close_input(&weak_context);
 -                                      BOOST_THROW_EXCEPTION(ffmpeg_error() << msg_info(unsupported_tokens));
 -                              }
 -                              av_dict_free(&format_options);
 -                      }
 -                      break;
 -              case FFMPEG_Resource::FFMPEG_STREAM:
 -                      {
 -                              AVDictionary* format_options = NULL;
 -                              for (auto& option : vid_params)
 -                              {
 -                                      av_dict_set(&format_options, option.first.c_str(), option.second.c_str(), 0);
 -                              }
 -                              THROW_ON_ERROR2(avformat_open_input(&weak_context, u8(resource_name).c_str(), nullptr, &format_options), resource_name);
 -                              if (format_options != nullptr)
 -                              {
 -                                      std::string unsupported_tokens = "";
 -                                      AVDictionaryEntry *t = NULL;
 -                                      while ((t = av_dict_get(format_options, "", t, AV_DICT_IGNORE_SUFFIX)) != nullptr)
 -                                      {
 -                                              if (!unsupported_tokens.empty())
 -                                                      unsupported_tokens += ", ";
 -                                              unsupported_tokens += t->key;
 -                                      }
 -                                      avformat_close_input(&weak_context);
 -                                      BOOST_THROW_EXCEPTION(ffmpeg_error() << msg_info(unsupported_tokens));
 -                              }
 +              CASPAR_SCOPE_EXIT
 +              {
 +                      if (format_options)
                                av_dict_free(&format_options);
 -                      }
 -                      break;
                };
 -              spl::shared_ptr<AVFormatContext> context(weak_context, [](AVFormatContext* p)
 +
 +              for (auto& option : vid_params)
 +                      av_dict_set(&format_options, option.first.c_str(), option.second.c_str(), 0);
 +
 +              auto resource_name                      = std::wstring();
 +              auto parts                                      = caspar::protocol_split(url_or_file);
 +              AVInputFormat* input_format     = nullptr;
 +
 +              if (parts.at(0).empty())
 +                      resource_name = parts.at(1);
 +              else if (parts.at(0) == L"dshow")
 +              {
 +                      input_format = av_find_input_format("dshow");
 +                      resource_name = parts.at(1);
 +              }
 +              else
 +                      resource_name = parts.at(0) + L"://" + parts.at(1);
 +
 +              AVFormatContext* weak_context = nullptr;
 +              THROW_ON_ERROR2(avformat_open_input(&weak_context, u8(resource_name).c_str(), input_format, &format_options), resource_name);
 +
 +              spl::shared_ptr<AVFormatContext> context(weak_context, [](AVFormatContext* ptr)
                {
 -                      avformat_close_input(&p);
 +                      avformat_close_input(&ptr);
                });
 -              THROW_ON_ERROR2(avformat_find_stream_info(weak_context, nullptr), resource_name);
 +
 +              if (format_options)
 +              {
 +                      std::string unsupported_tokens = "";
 +                      AVDictionaryEntry *t = NULL;
 +                      while ((t = av_dict_get(format_options, "", t, AV_DICT_IGNORE_SUFFIX)) != nullptr)
 +                      {
 +                              if (!unsupported_tokens.empty())
 +                                      unsupported_tokens += ", ";
 +                              unsupported_tokens += t->key;
 +                      }
 +                      CASPAR_THROW_EXCEPTION(user_error() << msg_info(unsupported_tokens));
 +              }
 +
 +              THROW_ON_ERROR2(avformat_find_stream_info(context.get(), nullptr), resource_name);
                fix_meta_data(*context);
                return context;
        }
        }
  };
  
 -input::input(const spl::shared_ptr<diagnostics::graph>& graph, const std::wstring& filename, FFMPEG_Resource resource_type, bool loop, uint32_t start, uint32_t length, bool thumbnail_mode, const ffmpeg_options& vid_params)
 -      : impl_(new implementation(graph, filename, resource_type, loop, start, length, thumbnail_mode, vid_params)){}
 +input::input(const spl::shared_ptr<diagnostics::graph>& graph, const std::wstring& url_or_file, bool loop, uint32_t start, uint32_t length, bool thumbnail_mode, const ffmpeg_options& vid_params)
 +      : impl_(new implementation(graph, url_or_file, loop, start, length, thumbnail_mode, vid_params)){}
  bool input::eof() const {return !impl_->executor_.is_running();}
  bool input::try_pop(std::shared_ptr<AVPacket>& packet){return impl_->try_pop(packet);}
  spl::shared_ptr<AVFormatContext> input::context(){return impl_->format_context_;}
index c8a1fc8609699ae69b7ab858ff05f44f06d3d591,bb15ee47cfebcd359b8b429e4964a4387bd49094..cc21e88e4cfc10288d43c8453af7683461905527
@@@ -19,7 -19,7 +19,7 @@@
  * Author: Robert Nagy, ronag89@gmail.com
  */
  
- #include "../../stdafx.h"
+ #include "../../StdAfx.h"
  
  #include "video_decoder.h"
  
@@@ -103,7 -103,7 +103,7 @@@ public
                        packets_.pop();
                        file_frame_number_ = static_cast<uint32_t>(packet->pos);
                        avcodec_flush_buffers(codec_context_.get());
 -                      return flush_video();   
 +                      return flush_video();
                }
                        
                packets_.pop();
@@@ -167,4 -167,4 +167,4 @@@ uint32_t video_decoder::file_frame_numb
  bool  video_decoder::is_progressive() const{return impl_->is_progressive_;}
  std::wstring video_decoder::print() const{return impl_->print();}
  
- }}
+ }}