1 #include "../../stdafx.h"
\r
3 #include "ffmpeg_producer.h"
\r
5 #if defined(_MSC_VER)
\r
6 #pragma warning (push)
\r
7 #pragma warning (disable : 4244)
\r
12 #define __STDC_CONSTANT_MACROS
\r
13 #define __STDC_LIMIT_MACROS
\r
14 #include <libavcodec/avcodec.h>
\r
15 #include <libavformat/avformat.h>
\r
16 #include <libavutil/avutil.h>
\r
17 #include <libswscale/swscale.h>
\r
20 #if defined(_MSC_VER)
\r
21 #pragma warning (pop)
\r
26 #include "audio/audio_decoder.h"
\r
27 #include "video/video_decoder.h"
\r
28 #include "video/video_transformer.h"
\r
30 #include "../../format/video_format.h"
\r
31 #include "../../../common/utility/scope_exit.h"
\r
32 #include "../../server.h"
\r
34 #include <tbb/mutex.h>
\r
35 #include <tbb/parallel_invoke.h>
\r
36 #include <tbb/task_group.h>
\r
38 #include <boost/algorithm/string/case_conv.hpp>
\r
39 #include <boost/lexical_cast.hpp>
\r
40 #include <boost/thread.hpp>
\r
41 #include <boost/thread/once.hpp>
\r
43 using namespace boost::assign;
\r
45 namespace caspar { namespace core { namespace ffmpeg{
\r
47 struct ffmpeg_producer : public frame_producer
\r
50 ffmpeg_producer(const std::wstring& filename, const std::vector<std::wstring>& params)
\r
51 : filename_(filename), underrun_count_(0)
\r
53 if(!boost::filesystem::exists(filename))
\r
54 BOOST_THROW_EXCEPTION(file_not_found() << boost::errinfo_file_name(common::narrow(filename)));
\r
56 static boost::once_flag av_register_all_flag = BOOST_ONCE_INIT;
\r
57 boost::call_once(av_register_all, av_register_all_flag);
\r
59 static boost::once_flag avcodec_init_flag = BOOST_ONCE_INIT;
\r
60 boost::call_once(avcodec_init, avcodec_init_flag);
\r
62 input_.reset(new input());
\r
63 input_->set_loop(std::find(params.begin(), params.end(), L"LOOP") != params.end());
\r
64 input_->load(common::narrow(filename_));
\r
65 video_decoder_.reset(new video_decoder(input_->get_video_codec_context().get()));
\r
66 video_transformer_.reset(new video_transformer(input_->get_video_codec_context().get()));
\r
67 audio_decoder_.reset(new audio_decoder(input_->get_audio_codec_context().get()));
\r
68 has_audio_ = input_->get_audio_codec_context() != nullptr;
\r
70 auto seek = std::find(params.begin(), params.end(), L"SEEK");
\r
71 if(seek != params.end() && ++seek != params.end())
\r
73 if(!input_->seek(boost::lexical_cast<unsigned long long>(*seek)))
\r
74 CASPAR_LOG(warning) << "Failed to seek file: " << filename_ << "to frame" << *seek;
\r
78 void initialize(const frame_processor_device_ptr& frame_processor)
\r
80 video_transformer_->initialize(frame_processor);
\r
83 frame_ptr render_frame()
\r
85 while(ouput_channel_.empty() && !input_->is_eof())
\r
87 auto video_packet = input_->get_video_packet();
\r
88 auto audio_packet = input_->get_audio_packet();
\r
89 tbb::parallel_invoke(
\r
91 { // Video Decoding and Scaling
\r
92 if(!video_packet.empty())
\r
94 auto decoded_frame = video_decoder_->execute(video_packet);
\r
95 auto transformed_frame = video_transformer_->execute(decoded_frame);
\r
96 video_frame_channel_.push_back(transformed_frame);
\r
100 { // Audio Decoding
\r
101 if(!audio_packet.empty())
\r
103 auto chunks = audio_decoder_->execute(audio_packet);
\r
104 audio_chunk_channel_.insert(audio_chunk_channel_.end(), chunks.begin(), chunks.end());
\r
108 if(video_packet.empty() && audio_packet.empty())
\r
110 if(underrun_count_++ == 0)
\r
111 CASPAR_LOG(warning) << "### File read underflow has STARTED.";
\r
113 // Return last frame without audio.
\r
114 last_frame_->audio_data().clear();
\r
115 return last_frame_;
\r
117 else if(underrun_count_ > 0)
\r
119 CASPAR_LOG(trace) << "### File Read Underrun has ENDED with " << underrun_count_ << " ticks.";
\r
120 underrun_count_ = 0;
\r
123 while(!video_frame_channel_.empty() && (!audio_chunk_channel_.empty() || !has_audio_))
\r
125 if(has_audio_ && video_frame_channel_.front() != nullptr)
\r
127 video_frame_channel_.front()->audio_data() = std::move(audio_chunk_channel_.front());
\r
128 audio_chunk_channel_.pop_front();
\r
131 frame_ptr frame = video_frame_channel_.front();
\r
132 video_frame_channel_.pop_front();
\r
133 ouput_channel_.push(std::move(frame));
\r
137 if(!ouput_channel_.empty())
\r
139 last_frame_ = ouput_channel_.front();
\r
140 ouput_channel_.pop();
\r
142 else if(input_->is_eof())
\r
143 last_frame_ = nullptr;
\r
145 return last_frame_;
\r
148 std::wstring print()
\r
150 std::wstringstream str;
\r
151 str << L"ffmpeg_producer " << filename_ << L".";
\r
157 input_uptr input_;
\r
159 video_decoder_uptr video_decoder_;
\r
160 video_transformer_uptr video_transformer_;
\r
161 std::deque<frame_ptr> video_frame_channel_;
\r
163 audio_decoder_ptr audio_decoder_;
\r
164 std::deque<std::vector<short>> audio_chunk_channel_;
\r
166 std::queue<frame_ptr> ouput_channel_;
\r
168 std::wstring filename_;
\r
170 long underrun_count_;
\r
172 frame_ptr last_frame_;
\r
175 frame_producer_ptr create_ffmpeg_producer(const std::vector<std::wstring>& params)
\r
177 static const std::vector<std::wstring> extensions = list_of(L"mpg")(L"avi")(L"mov")(L"dv")(L"wav")(L"mp3")(L"mp4")(L"f4v")(L"flv");
\r
178 std::wstring filename = server::media_folder() + L"\\" + params[0];
\r
180 auto ext = std::find_if(extensions.begin(), extensions.end(), [&](const std::wstring& ex) -> bool
\r
182 return boost::filesystem::is_regular_file(boost::filesystem::wpath(filename).replace_extension(ex));
\r
185 if(ext == extensions.end())
\r
188 return std::make_shared<ffmpeg_producer>(filename + L"." + *ext, params);
\r