]> git.sesse.net Git - casparcg/commitdiff
2.0.0.2: ffmpeg_input: Refactoring. Extracted "stream" class from input.
authorronag <ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Sat, 21 May 2011 20:53:21 +0000 (20:53 +0000)
committerronag <ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Sat, 21 May 2011 20:53:21 +0000 (20:53 +0000)
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches/2.0.0.2@799 362d55ac-95cf-4e76-9f9a-cbaa9c17b72d

modules/ffmpeg/producer/input.cpp

index 16f77428fdeb92d9167d8a11ed2659d35ece2749..421b9cb0658eb9b38814a4ef90a42349669b7cb0 100644 (file)
@@ -46,26 +46,91 @@ extern "C"
 }\r
 \r
 namespace caspar {\r
+       \r
+static const size_t PACKET_BUFFER_COUNT = 100; // Assume that av_read_frame distance between audio and video packets is less than PACKET_BUFFER_COUNT.\r
+\r
+class stream\r
+{\r
+       std::shared_ptr<AVCodecContext> ctx_;\r
+       int index_;\r
+       tbb::concurrent_bounded_queue<std::shared_ptr<AVPacket>> buffer_;\r
+\r
+public:\r
+\r
+       stream()\r
+               : index_(-1)\r
+       {\r
+               buffer_.set_capacity(PACKET_BUFFER_COUNT);\r
+       }\r
+\r
+       int open(std::shared_ptr<AVFormatContext>& fctx, AVMediaType media_type)\r
+       {               \r
+               const auto streams = boost::iterator_range<AVStream**>(fctx->streams, fctx->streams+fctx->nb_streams);\r
+               const auto stream = boost::find_if(streams, [&](AVStream* stream) \r
+               {\r
+                       return stream && stream->codec->codec_type == media_type;\r
+               });\r
+               \r
+               if(stream == streams.end()) \r
+                       return AVERROR_STREAM_NOT_FOUND;\r
+               \r
+               auto codec = avcodec_find_decoder((*stream)->codec->codec_id);                  \r
+               if(!codec)\r
+                       return AVERROR_DECODER_NOT_FOUND;\r
+                       \r
+               index_ = (*stream)->index;\r
+\r
+               int errn = tbb_avcodec_open((*stream)->codec, codec);\r
+               if(errn >= 0)\r
+               {\r
+                       ctx_.reset((*stream)->codec, tbb_avcodec_close);\r
+\r
+                       // Some files give an invalid time_base numerator, try to fix it.\r
+                       if(ctx_ && ctx_->time_base.num == 1)\r
+                               ctx_->time_base.num = static_cast<int>(std::pow(10.0, static_cast<int>(std::log10(static_cast<float>(ctx_->time_base.den)))-1));\r
+               }\r
+               return errn;    \r
+       }\r
+\r
+       std::shared_ptr<AVPacket> pop()\r
+       {\r
+               std::shared_ptr<AVPacket> pkt;\r
+               buffer_.try_pop(pkt);\r
+               return pkt;\r
+       }\r
+\r
+       void push(const std::shared_ptr<AVPacket>& pkt)\r
+       {\r
+               if(pkt->stream_index != index_)\r
+                       return;\r
+\r
+               av_dup_packet(pkt.get());\r
+               buffer_.push(pkt);      \r
+       }\r
+\r
+       const std::shared_ptr<AVCodecContext>& ctx() { return ctx_; }\r
+\r
+       operator bool(){return ctx_ != nullptr;}\r
+\r
+       double fps() const { return !ctx_ ? -1.0 : static_cast<double>(ctx_->time_base.den) / static_cast<double>(ctx_->time_base.num); }\r
+\r
+       bool empty() const { return buffer_.empty();}\r
+       int size() const { return buffer_.size();}\r
+};\r
                \r
 struct input::implementation : boost::noncopyable\r
 {              \r
-       static const size_t PACKET_BUFFER_COUNT = 100; // Assume that av_read_frame distance between audio and video packets is less than PACKET_BUFFER_COUNT.\r
-\r
        safe_ptr<diagnostics::graph> graph_;\r
 \r
        std::shared_ptr<AVFormatContext> format_context_;       // Destroy this last\r
-\r
-       std::shared_ptr<AVCodecContext> video_codec_context_;\r
-       std::shared_ptr<AVCodecContext> audio_codex_context_;\r
-       \r
+               \r
        const std::wstring      filename_;\r
        const bool                      loop_;\r
-       int                                     video_s_index_;\r
-       int                                     audio_s_index_;\r
-       const int                       start_;\r
-               \r
-       tbb::concurrent_bounded_queue<std::shared_ptr<AVPacket>> video_packet_buffer_;\r
-       tbb::concurrent_bounded_queue<std::shared_ptr<AVPacket>> audio_packet_buffer_;\r
+       const int                       start_;         \r
+       double                          fps_;\r
+\r
+       stream video_stream_;\r
+       stream audio_stream_;\r
                \r
        std::exception_ptr exception_;\r
        executor executor_;\r
@@ -73,8 +138,6 @@ public:
        explicit implementation(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, int start) \r
                : graph_(graph)\r
                , loop_(loop)\r
-               , video_s_index_(-1)\r
-               , audio_s_index_(-1)\r
                , filename_(filename)\r
                , executor_(print())\r
                , start_(std::max(start, 0))\r
@@ -110,15 +173,15 @@ public:
                                boost::errinfo_errno(AVUNERROR(errn)));\r
                }\r
                \r
-               errn = open_stream(video_codec_context_, AVMEDIA_TYPE_VIDEO, video_s_index_);\r
-               if(errn < 0 || !video_codec_context_)\r
+               errn = video_stream_.open(format_context_, AVMEDIA_TYPE_VIDEO);\r
+               if(errn < 0)\r
                        CASPAR_LOG(warning) << print() << L" Could not open video stream: " << widen(av_error_str(errn));\r
                \r
-               errn = open_stream(audio_codex_context_, AVMEDIA_TYPE_AUDIO, audio_s_index_);\r
-               if(errn < 0 || !audio_codex_context_)\r
+               errn = audio_stream_.open(format_context_, AVMEDIA_TYPE_AUDIO);\r
+               if(errn < 0)\r
                        CASPAR_LOG(warning) << print() << L" Could not open audio stream: " << widen(av_error_str(errn));\r
                \r
-               if(!video_codec_context_ && !audio_codex_context_)\r
+               if(!video_stream_ && !audio_stream_)\r
                {       \r
                        BOOST_THROW_EXCEPTION(\r
                                file_read_error() << \r
@@ -126,9 +189,8 @@ public:
                                msg_info("No video or audio codec context found."));    \r
                }\r
 \r
-               video_packet_buffer_.set_capacity(PACKET_BUFFER_COUNT);\r
-               audio_packet_buffer_.set_capacity(PACKET_BUFFER_COUNT);\r
-               \r
+               fps_ = video_stream_ ? video_stream_.fps() : audio_stream_.fps();\r
+\r
                if(start_ != 0)                 \r
                        seek_frame(start_);\r
                                        \r
@@ -144,22 +206,22 @@ public:
                \r
        std::shared_ptr<AVPacket> get_video_packet()\r
        {\r
-               return get_packet(video_packet_buffer_);\r
+               return video_stream_.pop();\r
        }\r
 \r
        std::shared_ptr<AVPacket> get_audio_packet()\r
        {\r
-               return get_packet(audio_packet_buffer_);\r
+               return audio_stream_.pop();\r
        }\r
 \r
        bool has_packet() const\r
        {\r
-               return !video_packet_buffer_.empty() || !audio_packet_buffer_.empty();\r
+               return !video_stream_.empty() || !audio_stream_.empty();\r
        }\r
                                \r
        double fps()\r
        {\r
-               return static_cast<double>(get_default_context()->time_base.den) / static_cast<double>(get_default_context()->time_base.num);\r
+               return fps_;\r
        }\r
 \r
 private:\r
@@ -172,44 +234,10 @@ private:
                CASPAR_LOG(info) << print() << " Stopping.";\r
        }\r
                        \r
-       int open_stream(std::shared_ptr<AVCodecContext>& ctx, int codec_type, int& s_index) const\r
-       {               \r
-               const auto streams = boost::iterator_range<AVStream**>(format_context_->streams, format_context_->streams+format_context_->nb_streams);\r
-               const auto stream = boost::find_if(streams, [&](AVStream* stream) \r
-               {\r
-                       return stream != nullptr && stream->codec->codec_type == codec_type;\r
-               });\r
-               \r
-               if(stream == streams.end()) \r
-                       return AVERROR_STREAM_NOT_FOUND;\r
-               \r
-               auto codec = avcodec_find_decoder((*stream)->codec->codec_id);                  \r
-               if(codec == nullptr)\r
-                       return AVERROR_DECODER_NOT_FOUND;\r
-                       \r
-               s_index = (*stream)->index;\r
-\r
-               int errn = tbb_avcodec_open((*stream)->codec, codec);\r
-               if(errn >= 0)\r
-               {\r
-                       ctx.reset((*stream)->codec, tbb_avcodec_close);\r
-\r
-                       // Some files give an invalid time_base numerator, try to fix it.\r
-                       if(ctx && ctx->time_base.num == 1)\r
-                               ctx->time_base.num = static_cast<int>(std::pow(10.0, static_cast<int>(std::log10(static_cast<float>(ctx->time_base.den)))-1));\r
-               }\r
-               return errn;    \r
-       }\r
-       \r
-       std::shared_ptr<AVCodecContext>& get_default_context()\r
-       {\r
-               return video_codec_context_ ? video_codec_context_ : audio_codex_context_;\r
-       }\r
-               \r
        void read_file()\r
        {               \r
-               if(audio_packet_buffer_.size() > 4 && video_packet_buffer_.size() > 4)\r
-                       boost::this_thread::sleep(boost::posix_time::millisec(5)); // There are enough packets, no hurry.\r
+               if(audio_stream_.size() > 4 && video_stream_.size() > 4)\r
+                       boost::this_thread::yield(); // There are enough packets, no hurry.\r
 \r
                try\r
                {\r
@@ -245,13 +273,11 @@ private:
                        }\r
                        else\r
                        {\r
-                               if(read_packet->stream_index == video_s_index_)                 \r
-                                       push_packet(video_packet_buffer_, read_packet);\r
-                               else if(read_packet->stream_index == audio_s_index_)    \r
-                                       push_packet(audio_packet_buffer_, read_packet);\r
+                               video_stream_.push(read_packet);\r
+                               audio_stream_.push(read_packet);\r
                        }\r
                                                \r
-                       graph_->update_value("input-buffer", static_cast<float>(video_packet_buffer_.size())/static_cast<float>(PACKET_BUFFER_COUNT));          \r
+                       graph_->update_value("input-buffer", static_cast<float>(std::max(video_stream_.size(), audio_stream_.size()))/static_cast<float>(PACKET_BUFFER_COUNT));         \r
                }\r
                catch(...)\r
                {\r
@@ -266,7 +292,7 @@ private:
        void seek_frame(int64_t frame, int flags = 0)\r
        {       \r
                // Convert from frames into seconds.\r
-               const auto ts = frame*static_cast<int64_t>((AV_TIME_BASE*get_default_context()->time_base.num) / get_default_context()->time_base.den);\r
+               const auto ts = frame*static_cast<int64_t>(AV_TIME_BASE/fps_);\r
 \r
                const int errn = av_seek_frame(format_context_.get(), -1, ts, flags | AVSEEK_FLAG_FRAME);\r
                if(errn < 0)\r
@@ -288,19 +314,6 @@ private:
                return errn == AVERROR_EOF || errn == AVERROR(EIO); // av_read_frame doesn't always correctly return AVERROR_EOF;\r
        }\r
        \r
-       void push_packet(tbb::concurrent_bounded_queue<std::shared_ptr<AVPacket>>& buffer, const std::shared_ptr<AVPacket>& read_packet)\r
-       {\r
-               av_dup_packet(read_packet.get());\r
-               buffer.push(read_packet);       \r
-       }\r
-\r
-       std::shared_ptr<AVPacket> get_packet(tbb::concurrent_bounded_queue<std::shared_ptr<AVPacket>>& buffer)\r
-       {\r
-               std::shared_ptr<AVPacket> packet;\r
-               buffer.try_pop(packet);\r
-               return packet;\r
-       }\r
-\r
        std::wstring print() const\r
        {\r
                return L"ffmpeg_input[" + filename_ + L"]";\r
@@ -309,8 +322,8 @@ private:
 \r
 input::input(const safe_ptr<diagnostics::graph>& graph, const std::wstring& filename, bool loop, int start) \r
        : impl_(new implementation(graph, filename, loop, start)){}\r
-const std::shared_ptr<AVCodecContext>& input::get_video_codec_context() const{return impl_->video_codec_context_;}\r
-const std::shared_ptr<AVCodecContext>& input::get_audio_codec_context() const{return impl_->audio_codex_context_;}\r
+const std::shared_ptr<AVCodecContext>& input::get_video_codec_context() const{return impl_->video_stream_.ctx();}\r
+const std::shared_ptr<AVCodecContext>& input::get_audio_codec_context() const{return impl_->audio_stream_.ctx();}\r
 bool input::has_packet() const{return impl_->has_packet();}\r
 bool input::is_running() const {return impl_->executor_.is_running();}\r
 std::shared_ptr<AVPacket> input::get_video_packet(){return impl_->get_video_packet();}\r