]> git.sesse.net Git - casparcg/commitdiff
2.0.0.2: ffmpeg_producer: Improved error-handling.
authorronag <ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Sat, 14 May 2011 09:51:43 +0000 (09:51 +0000)
committerronag <ronag@362d55ac-95cf-4e76-9f9a-cbaa9c17b72d>
Sat, 14 May 2011 09:51:43 +0000 (09:51 +0000)
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches/2.0.0.2@738 362d55ac-95cf-4e76-9f9a-cbaa9c17b72d

modules/ffmpeg/ffmpeg_error.h [new file with mode: 0644]
modules/ffmpeg/producer/audio/audio_decoder.cpp
modules/ffmpeg/producer/audio/audio_decoder.h
modules/ffmpeg/producer/ffmpeg_producer.cpp
modules/ffmpeg/producer/input.cpp
modules/ffmpeg/producer/video/video_decoder.cpp
shell/caspar.config

diff --git a/modules/ffmpeg/ffmpeg_error.h b/modules/ffmpeg/ffmpeg_error.h
new file mode 100644 (file)
index 0000000..c66f9e4
--- /dev/null
@@ -0,0 +1,25 @@
+#pragma once\r
+\r
+#include <string>\r
+\r
+#pragma warning(push, 1)\r
+\r
+extern "C" \r
+{\r
+#include <libavutil/error.h>\r
+}\r
+\r
+#pragma warning(pop)\r
+\r
+namespace caspar {\r
+\r
+static std::string av_error_str(int errn)\r
+{\r
+       char buf[256];\r
+       memset(buf, 0, 256);\r
+       if(av_strerror(errn, buf, 256) < 0)\r
+               return "";\r
+       return std::string(buf);\r
+}\r
+\r
+}
\ No newline at end of file
index 0919bb79c6c63e4ab83f7f1696cd4c3a73ac077a..311df2f478f96684e0b928beefbb9632d6ba3537 100644 (file)
@@ -43,23 +43,32 @@ struct audio_decoder::implementation : boost::noncopyable
        typedef std::vector<short, tbb::cache_aligned_allocator<short>> buffer;\r
        \r
        AVCodecContext* codec_context_;\r
+               \r
+       const core::video_format_desc format_desc_;\r
 \r
-       buffer audio_buffer_;   \r
        buffer current_chunk_;\r
 \r
-       const size_t audio_frame_size_;\r
-\r
-       static const size_t SAMPLE_RATE = 48000;\r
-       static const size_t N_CHANNELS = 2;\r
-\r
 public:\r
-       explicit implementation(AVCodecContext* codec_context, double fps\r
+       explicit implementation(AVCodecContext* codec_context, const core::video_format_desc& format_desc\r
                : codec_context_(codec_context)\r
-               , audio_buffer_(4*SAMPLE_RATE*2+FF_INPUT_BUFFER_PADDING_SIZE/2)\r
-               , audio_frame_size_(static_cast<size_t>(static_cast<double>(SAMPLE_RATE) / fps) * N_CHANNELS)\r
+               , format_desc_(format_desc)             \r
        {\r
                if(!codec_context)\r
-                       BOOST_THROW_EXCEPTION(null_argument() << arg_name_info("codec_context"));                                               \r
+               {       \r
+                       BOOST_THROW_EXCEPTION(\r
+                               null_argument() << \r
+                               arg_name_info("codec_context"));        \r
+               }\r
+\r
+               if(codec_context_->sample_rate != static_cast<int>(format_desc_.audio_sample_rate) || \r
+                  codec_context_->channels != static_cast<int>(format_desc_.audio_channels))\r
+               {       \r
+                       BOOST_THROW_EXCEPTION(\r
+                               file_read_error()  <<\r
+                               msg_info("Invalid sample-rate or number of channels.") <<\r
+                               arg_value_info(boost::lexical_cast<std::string>(codec_context_->sample_rate)) << \r
+                               arg_name_info("codec_context"));\r
+               }\r
        }\r
                \r
        std::vector<std::vector<short>> execute(const std::shared_ptr<aligned_buffer>& audio_packet)\r
@@ -73,10 +82,12 @@ public:
                        return std::vector<std::vector<short>>();\r
                }\r
 \r
-               int written_bytes = audio_buffer_.size()*2 - FF_INPUT_BUFFER_PADDING_SIZE;\r
-               const int errn = avcodec_decode_audio2(codec_context_, audio_buffer_.data(), &written_bytes, audio_packet->data(), audio_packet->size());\r
-\r
-               if(errn < 0 || codec_context_->sample_rate != SAMPLE_RATE || codec_context_->channels != 2)\r
+               auto s = current_chunk_.size();\r
+               current_chunk_.resize(s + 4*format_desc_.audio_sample_rate*2+FF_INPUT_BUFFER_PADDING_SIZE/2);\r
+               \r
+               int written_bytes = (current_chunk_.size() - s)*2 - FF_INPUT_BUFFER_PADDING_SIZE;\r
+               const int errn = avcodec_decode_audio2(codec_context_, &current_chunk_[s], &written_bytes, audio_packet->data(), audio_packet->size());\r
+               if(errn < 0)\r
                {       \r
                        BOOST_THROW_EXCEPTION(\r
                                invalid_operation() <<\r
@@ -84,14 +95,13 @@ public:
                                boost::errinfo_errno(AVUNERROR(errn)));\r
                }\r
 \r
-               current_chunk_.insert(current_chunk_.end(), audio_buffer_.data(), audio_buffer_.data() + written_bytes/2);\r
+               current_chunk_.resize(s + written_bytes/2);\r
 \r
+               const auto last = current_chunk_.end() - current_chunk_.size() % format_desc_.audio_samples_per_frame;\r
+               \r
                std::vector<std::vector<short>> chunks;\r
-                               \r
-               const auto last = current_chunk_.end() - current_chunk_.size() % audio_frame_size_;\r
-\r
-               for(auto it = current_chunk_.begin(); it != last; it += audio_frame_size_)              \r
-                       chunks.push_back(std::vector<short>(it, it + audio_frame_size_));               \r
+               for(auto it = current_chunk_.begin(); it != last; it += format_desc_.audio_samples_per_frame)           \r
+                       chunks.push_back(std::vector<short>(it, it + format_desc_.audio_samples_per_frame));            \r
 \r
                current_chunk_.erase(current_chunk_.begin(), last);\r
                \r
@@ -99,6 +109,6 @@ public:
        }\r
 };\r
 \r
-audio_decoder::audio_decoder(AVCodecContext* codec_context, double fps) : impl_(new implementation(codec_context, fps)){}\r
+audio_decoder::audio_decoder(AVCodecContext* codec_context, const core::video_format_desc& format_desc) : impl_(new implementation(codec_context, format_desc)){}\r
 std::vector<std::vector<short>> audio_decoder::execute(const std::shared_ptr<aligned_buffer>& audio_packet){return impl_->execute(audio_packet);}\r
 }
\ No newline at end of file
index aa04c45709839282272146f75c31a888a512fc3d..fc25d386cec218f3a11fdba96147c7dd18d2dcea 100644 (file)
@@ -19,6 +19,8 @@
 */\r
 #pragma once\r
 \r
+#include <core/video_format.h>\r
+\r
 #include <tbb/cache_aligned_allocator.h>\r
 \r
 #include <boost/noncopyable.hpp>\r
@@ -35,7 +37,7 @@ typedef std::vector<unsigned char, tbb::cache_aligned_allocator<unsigned char>>
 class audio_decoder : boost::noncopyable\r
 {\r
 public:\r
-       explicit audio_decoder(AVCodecContext* codec_context, double fps);\r
+       explicit audio_decoder(AVCodecContext* codec_context, const core::video_format_desc& format_desc);\r
        std::vector<std::vector<short>> execute(const std::shared_ptr<aligned_buffer>& audio_packet);\r
 private:\r
        struct implementation;\r
index 7229997e406839683981755cb58ecca7499a8446..db7e0060254ca9f06f32d56f848b804b5d7d7ee9 100644 (file)
@@ -71,8 +71,6 @@ public:
                , graph_(diagnostics::create_graph(narrow(print())))\r
                , frame_factory_(frame_factory)         \r
                , input_(safe_ptr<diagnostics::graph>(graph_), filename_, loop_, start_frame, end_frame)\r
-               , video_decoder_(input_.get_video_codec_context().get() ? new video_decoder(input_.get_video_codec_context().get(), frame_factory) : nullptr)\r
-               , audio_decoder_(input_.get_audio_codec_context().get() ? new audio_decoder(input_.get_audio_codec_context().get(), frame_factory->get_video_format_desc().fps) : nullptr)\r
        {\r
                graph_->add_guide("frame-time", 0.5);\r
                graph_->set_color("frame-time",  diagnostics::color(1.0f, 0.0f, 0.0f));\r
@@ -82,6 +80,37 @@ public:
                double format_frame_time = 1.0/frame_factory->get_video_format_desc().fps;\r
                if(abs(frame_time - format_frame_time) > 0.0001)\r
                        CASPAR_LOG(warning) << print() << L" Invalid framerate detected. This may cause distorted audio during playback. frame-time: " << frame_time;\r
+\r
+               try\r
+               {                       \r
+                       video_decoder_.reset(input_.get_video_codec_context().get() ? \r
+                               new video_decoder(input_.get_video_codec_context().get(), frame_factory) : nullptr);\r
+               }\r
+               catch(...)\r
+               {\r
+                       CASPAR_LOG_CURRENT_EXCEPTION();\r
+                       video_decoder_.reset();\r
+                       CASPAR_LOG(warning) << print() << " removed video-stream.";\r
+               }\r
+               \r
+               try\r
+               {                       \r
+                       audio_decoder_.reset(input_.get_audio_codec_context().get() ? \r
+                               new audio_decoder(input_.get_audio_codec_context().get(), frame_factory->get_video_format_desc()) : nullptr);\r
+               }\r
+               catch(...)\r
+               {\r
+                       CASPAR_LOG_CURRENT_EXCEPTION();\r
+                       audio_decoder_.reset();\r
+                       CASPAR_LOG(warning) << print() << " removed audio-stream.";\r
+               }               \r
+\r
+               if(!video_decoder_ && !audio_decoder_)\r
+               {\r
+                       BOOST_THROW_EXCEPTION(\r
+                               caspar_exception() <<\r
+                               msg_info("Failed to initialize any decoder"));\r
+               }\r
        }\r
 \r
        virtual safe_ptr<core::basic_frame> receive()\r
index df77ffcb69cb780592429c799eb38dd2afe1dda8..1ac339348d0c307ea2662418feed300801c0dd28 100644 (file)
 #include <common/diagnostics/graph.h>\r
 \r
 #include <tbb/concurrent_queue.h>\r
-#include <tbb/queuing_mutex.h>\r
+#include <tbb/mutex.h>\r
 \r
-#include <boost/exception/error_info.hpp>\r
-#include <boost/thread/once.hpp>\r
-#include <boost/thread/thread.hpp>\r
-#include <boost/regex.hpp>\r
-\r
-#include <errno.h>\r
-#include <system_error>\r
-               \r
 #if defined(_MSC_VER)\r
 #pragma warning (disable : 4244)\r
 #endif\r
@@ -62,20 +54,19 @@ struct input::implementation : boost::noncopyable
        std::shared_ptr<AVCodecContext> video_codec_context_;\r
        std::shared_ptr<AVCodecContext> audio_codex_context_;\r
        \r
-       const std::wstring filename_;\r
-\r
-       bool loop_;\r
-       int video_s_index_;\r
-       int     audio_s_index_;\r
-       const int start_frame_;\r
-       const int end_frame_;\r
-       int eof_count_;\r
+       const std::wstring      filename_;\r
+       const bool                      loop_;\r
+       int                                     video_s_index_;\r
+       int                                     audio_s_index_;\r
+       const int                       start_frame_;\r
+       const int                       end_frame_;\r
+       int                                     eof_count_;\r
                \r
        tbb::concurrent_bounded_queue<std::shared_ptr<aligned_buffer>> video_packet_buffer_;\r
        tbb::concurrent_bounded_queue<std::shared_ptr<aligned_buffer>> audio_packet_buffer_;\r
 \r
-       boost::condition_variable cond_;\r
-       boost::mutex mutex_;\r
+       boost::condition_variable       cond_;\r
+       boost::mutex                            mutex_;\r
        \r
        std::exception_ptr exception_;\r
        executor executor_;\r
@@ -92,10 +83,12 @@ public:
                , eof_count_(end_frame-start_frame)\r
        {                       \r
                if(end_frame_ > 0 && end_frame <= start_frame_)\r
+               {       \r
                        BOOST_THROW_EXCEPTION(\r
                                invalid_argument() << \r
                                source_info(narrow(print())) << \r
                                msg_info("End-frame cannot be lower than start-frame."));       \r
+               }\r
 \r
                graph_->set_color("input-buffer", diagnostics::color(1.0f, 1.0f, 0.0f));\r
                graph_->set_color("seek", diagnostics::color(0.5f, 1.0f, 0.5f));        \r
@@ -114,12 +107,14 @@ public:
                format_context_.reset(weak_format_context_, av_close_input_file);\r
                        \r
                if((errn = av_find_stream_info(format_context_.get())) < 0)\r
+               {       \r
                        BOOST_THROW_EXCEPTION(\r
                                file_read_error() << \r
                                source_info(narrow(print())) << \r
                                msg_info(av_error_str(errn)) <<\r
                                boost::errinfo_api_function("av_find_stream_info") <<\r
                                boost::errinfo_errno(AVUNERROR(errn)));\r
+               }\r
 \r
                video_codec_context_ = open_stream(CODEC_TYPE_VIDEO, video_s_index_);\r
                if(!video_codec_context_)\r
@@ -131,15 +126,17 @@ public:
                if(!audio_codex_context_)\r
                        CASPAR_LOG(warning) << print() << " Could not open any audio stream.";\r
                else\r
-                       fix_time_base(video_codec_context_.get());\r
+                       fix_time_base(audio_codex_context_.get());\r
 \r
                if(!video_codec_context_ && !audio_codex_context_)\r
+               {       \r
                        BOOST_THROW_EXCEPTION(\r
                                file_read_error() << \r
                                msg_info(av_error_str(errn)) <<\r
                                source_info(narrow(print())) << \r
                                msg_info("No video or audio codec context found."));    \r
-               \r
+               }\r
+\r
                if(start_frame_ != 0)                   \r
                        seek_frame(start_frame_);\r
                                        \r
@@ -153,20 +150,49 @@ public:
                executor_.clear();\r
                executor_.stop();\r
                cond_.notify_all();\r
+       }\r
+               \r
+       std::shared_ptr<aligned_buffer> get_video_packet()\r
+       {\r
+               return get_packet(video_packet_buffer_);\r
+       }\r
+\r
+       std::shared_ptr<aligned_buffer> get_audio_packet()\r
+       {\r
+               return get_packet(audio_packet_buffer_);\r
+       }\r
+\r
+       bool has_packet() const\r
+       {\r
+               return !video_packet_buffer_.empty() || !audio_packet_buffer_.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
+       }\r
+\r
+private:\r
+\r
+       void stop()\r
+       {\r
+               executor_.stop();\r
                CASPAR_LOG(info) << print() << " Stopped.";\r
        }\r
                        \r
-       void fix_time_base(AVCodecContext* context) // Some files give an invalid numerator, try to fix it.\r
+       void fix_time_base(AVCodecContext* context) const // Some files give an invalid numerator, try to fix it.\r
        {\r
                if(context && context->time_base.num == 1)\r
                        context->time_base.num = static_cast<int>(std::pow(10.0, static_cast<int>(std::log10(static_cast<float>(context->time_base.den)))-1));\r
        }\r
 \r
-       std::shared_ptr<AVCodecContext> open_stream(int codec_type, int& s_index)\r
+       std::shared_ptr<AVCodecContext> open_stream(int codec_type, int& s_index) const\r
        {               \r
                AVStream** streams_end = format_context_->streams+format_context_->nb_streams;\r
-               AVStream** stream = std::find_if(format_context_->streams, streams_end, \r
-                       [&](AVStream* stream) { return stream != nullptr && stream->codec->codec_type == codec_type ;});\r
+               AVStream** stream = std::find_if(format_context_->streams, streams_end, [&](AVStream* stream) \r
+               {\r
+                       return stream != nullptr && stream->codec->codec_type == codec_type;\r
+               });\r
                \r
                if(stream == streams_end) \r
                        return nullptr;\r
@@ -187,14 +213,6 @@ public:
        {\r
                return video_codec_context_ ? video_codec_context_ : audio_codex_context_;\r
        }\r
-\r
-       bool is_eof(int errn)\r
-       {\r
-               if(end_frame_ != -1)\r
-                       return get_default_context()->frame_number > eof_count_;                \r
-\r
-               return errn == AVERROR_EOF || errn == AVERROR_IO;\r
-       }\r
                \r
        void read_file()\r
        {               \r
@@ -203,8 +221,8 @@ public:
                        AVPacket tmp_packet;\r
                        safe_ptr<AVPacket> read_packet(&tmp_packet, av_free_packet);    \r
 \r
-                       auto read_frame_ret = av_read_frame(format_context_.get(), read_packet.get());\r
-                       if(is_eof(read_frame_ret))\r
+                       const int errn = av_read_frame(format_context_.get(), read_packet.get());\r
+                       if(is_eof(errn))\r
                        {\r
                                if(loop_)\r
                                {\r
@@ -216,22 +234,21 @@ public:
                                else\r
                                        stop();\r
                        }\r
-                       else if(read_frame_ret < 0)\r
+                       else if(errn < 0)\r
                        {\r
                                BOOST_THROW_EXCEPTION(\r
                                        invalid_operation() <<\r
-                                       msg_info(av_error_str(read_frame_ret)) <<\r
+                                       msg_info(av_error_str(errn)) <<\r
                                        source_info(narrow(print())) << \r
                                        boost::errinfo_api_function("av_read_frame") <<\r
-                                       boost::errinfo_errno(AVUNERROR(read_frame_ret)));\r
+                                       boost::errinfo_errno(AVUNERROR(errn)));\r
                        }\r
                        else\r
                        {\r
-                               auto packet = std::make_shared<aligned_buffer>(read_packet->data, read_packet->data + read_packet->size);\r
                                if(read_packet->stream_index == video_s_index_)                 \r
-                                       video_packet_buffer_.try_push(std::move(packet));       \r
+                                       video_packet_buffer_.try_push(std::make_shared<aligned_buffer>(read_packet->data, read_packet->data + read_packet->size));      \r
                                else if(read_packet->stream_index == audio_s_index_)    \r
-                                       audio_packet_buffer_.try_push(std::move(packet));       \r
+                                       audio_packet_buffer_.try_push(std::make_shared<aligned_buffer>(read_packet->data, read_packet->data + read_packet->size));      \r
                        }\r
                                                \r
                        graph_->update_value("input-buffer", static_cast<float>(video_packet_buffer_.size())/static_cast<float>(PACKET_BUFFER_COUNT));          \r
@@ -249,27 +266,6 @@ public:
                        cond_.wait(lock);               \r
        }\r
 \r
-       void stop()\r
-       {\r
-               executor_.stop();\r
-               CASPAR_LOG(info) << print() << " eof";\r
-       }\r
-               \r
-       std::shared_ptr<aligned_buffer> get_video_packet()\r
-       {\r
-               return get_packet(video_packet_buffer_);\r
-       }\r
-\r
-       std::shared_ptr<aligned_buffer> get_audio_packet()\r
-       {\r
-               return get_packet(audio_packet_buffer_);\r
-       }\r
-\r
-       bool has_packet() const\r
-       {\r
-               return !video_packet_buffer_.empty() || !audio_packet_buffer_.empty();\r
-       }\r
-       \r
        void seek_frame(int64_t frame, int flags = 0)\r
        {       \r
                // Convert from frames into seconds.\r
@@ -278,29 +274,35 @@ public:
                const int errn = av_seek_frame(format_context_.get(), -1, ts, flags | AVSEEK_FLAG_FRAME);\r
 \r
                if(errn < 0)\r
+               {       \r
                        BOOST_THROW_EXCEPTION(\r
                                invalid_operation() << \r
                                source_info(narrow(print())) << \r
                                msg_info(av_error_str(errn)) <<\r
                                boost::errinfo_api_function("seek_frame") <<\r
                                boost::errinfo_errno(AVUNERROR(errn)));\r
-               \r
+               }\r
+\r
                // Notify decoders to flush buffers.\r
                video_packet_buffer_.try_push(std::make_shared<aligned_buffer>());      \r
                audio_packet_buffer_.try_push(std::make_shared<aligned_buffer>());\r
        }\r
-       \r
+               \r
+\r
+       bool is_eof(int errn)\r
+       {\r
+               if(end_frame_ != -1)\r
+                       return get_default_context()->frame_number > eof_count_;                \r
+\r
+               return errn == AVERROR_EOF || errn == AVERROR_IO;\r
+       }\r
+               \r
        std::shared_ptr<aligned_buffer> get_packet(tbb::concurrent_bounded_queue<std::shared_ptr<aligned_buffer>>& buffer)\r
        {\r
                cond_.notify_all();\r
                std::shared_ptr<aligned_buffer> packet;\r
                return buffer.try_pop(packet) ? packet : nullptr;\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
-       }\r
 \r
        std::wstring print() const\r
        {\r
index cb721bad7373b1f703cbb716d372066af0dbdbda..50487bf52ceb4ef2bcf504ae652101cf74b4b962 100644 (file)
@@ -192,7 +192,7 @@ public:
                }\r
                else\r
                {\r
-                       // Uses sws_scale when we don't support the provided color-space.\r
+                       // Uses sws_scale when we don't support the provided colorspace.\r
                        safe_ptr<AVFrame> av_frame(avcodec_alloc_frame(), av_free);     \r
                        avcodec_get_frame_defaults(av_frame.get());                     \r
                        avpicture_fill(reinterpret_cast<AVPicture*>(av_frame.get()), write->image_data().begin(), PIX_FMT_BGRA, width_, height_);\r
index 895635706b1f4b96e331db4dcc83b54305d68389..950d5984c87f8d48086fbe0d2b5a26aa79b6d262 100644 (file)
     <channel>\r
       <videomode>1080i5000</videomode>\r
       <consumers>\r
-        <decklink>\r
+        <!--<decklink>\r
           <device>1</device>\r
           <embedded-audio>true</embedded-audio>\r
           <latency>low</latency>\r
           <key>external</key>\r
-        </decklink>\r
-        <!--<ogl>\r
-          <device>0</device>\r
+        </decklink>-->\r
+        <ogl>\r
+          <device>1</device>\r
           <stretch>uniform</stretch>\r
           <windowed>true</windowed>\r
-        </ogl>-->\r
-       <!-- <audio/>-->\r
+        </ogl>\r
+        <audio/>\r
         <!--<bluefish>\r
           <device>1</device>\r
           <embedded-audio>true</embedded-audio>\r