]> git.sesse.net Git - casparcg/blob - core/producer/ffmpeg/ffmpeg_producer.cpp
2.0.0.2:
[casparcg] / core / producer / ffmpeg / ffmpeg_producer.cpp
1 #include "../../stdafx.h"\r
2 \r
3 #include "ffmpeg_producer.h"\r
4 \r
5 #if defined(_MSC_VER)\r
6 #pragma warning (push)\r
7 #pragma warning (disable : 4244)\r
8 #endif\r
9 \r
10 extern "C" \r
11 {\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
18 }\r
19 \r
20 #if defined(_MSC_VER)\r
21 #pragma warning (pop)\r
22 #endif\r
23 \r
24 #include "input.h"\r
25 \r
26 #include "audio/audio_decoder.h"\r
27 #include "video/video_decoder.h"\r
28 #include "video/video_transformer.h"\r
29 \r
30 #include "../../format/video_format.h"\r
31 #include "../../../common/utility/scope_exit.h"\r
32 #include "../../server.h"\r
33 \r
34 #include <tbb/mutex.h>\r
35 #include <tbb/parallel_invoke.h>\r
36 #include <tbb/task_group.h>\r
37 \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
42 \r
43 using namespace boost::assign;\r
44 \r
45 namespace caspar { namespace core { namespace ffmpeg{\r
46         \r
47 struct ffmpeg_producer : public frame_producer\r
48 {\r
49 public:\r
50         ffmpeg_producer(const std::wstring& filename, const  std::vector<std::wstring>& params) \r
51                 : filename_(filename), underrun_count_(0)\r
52         {\r
53                 if(!boost::filesystem::exists(filename))\r
54                         BOOST_THROW_EXCEPTION(file_not_found() <<  boost::errinfo_file_name(common::narrow(filename)));\r
55                 \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
58                 \r
59                 static boost::once_flag avcodec_init_flag = BOOST_ONCE_INIT;\r
60                 boost::call_once(avcodec_init, avcodec_init_flag);      \r
61                                 \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
69 \r
70                 auto seek = std::find(params.begin(), params.end(), L"SEEK");\r
71                 if(seek != params.end() && ++seek != params.end())\r
72                 {\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
75                 }\r
76         }\r
77                 \r
78         void initialize(const frame_processor_device_ptr& frame_processor)\r
79         {\r
80                 video_transformer_->initialize(frame_processor);\r
81         }\r
82                 \r
83         frame_ptr render_frame()\r
84         {\r
85                 while(ouput_channel_.empty() && !input_->is_eof())\r
86                 {       \r
87                         auto video_packet = input_->get_video_packet();         \r
88                         auto audio_packet = input_->get_audio_packet();         \r
89                         tbb::parallel_invoke(\r
90                         [&]\r
91                         { // Video Decoding and Scaling\r
92                                 if(!video_packet.empty())\r
93                                 {\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
97                                 }\r
98                         }, \r
99                         [&] \r
100                         { // Audio Decoding\r
101                                 if(!audio_packet.empty())\r
102                                 {\r
103                                         auto chunks = audio_decoder_->execute(audio_packet);\r
104                                         audio_chunk_channel_.insert(audio_chunk_channel_.end(), chunks.begin(), chunks.end());\r
105                                 }\r
106                         });\r
107 \r
108                         if(video_packet.empty() && audio_packet.empty())\r
109                         {\r
110                                 if(underrun_count_++ == 0)\r
111                                         CASPAR_LOG(warning) << "### File read underflow has STARTED.";\r
112 \r
113                                 // Return last frame without audio.\r
114                                 last_frame_->audio_data().clear();\r
115                                 return last_frame_;\r
116                         }\r
117                         else if(underrun_count_ > 0)\r
118                         {\r
119                                 CASPAR_LOG(trace) << "### File Read Underrun has ENDED with " << underrun_count_ << " ticks.";\r
120                                 underrun_count_ = 0;\r
121                         }\r
122 \r
123                         while(!video_frame_channel_.empty() && (!audio_chunk_channel_.empty() || !has_audio_))\r
124                         {\r
125                                 if(has_audio_ && video_frame_channel_.front() != nullptr)\r
126                                 {\r
127                                         video_frame_channel_.front()->audio_data() = std::move(audio_chunk_channel_.front());\r
128                                         audio_chunk_channel_.pop_front();\r
129                                 }\r
130                                 \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
134                         }                               \r
135                 }\r
136 \r
137                 if(!ouput_channel_.empty())\r
138                 {\r
139                         last_frame_ = ouput_channel_.front();\r
140                         ouput_channel_.pop();\r
141                 }\r
142                 else if(input_->is_eof())\r
143                         last_frame_ = nullptr;\r
144 \r
145                 return last_frame_;\r
146         }\r
147 \r
148         std::wstring print()\r
149         {\r
150                 std::wstringstream str;\r
151                 str << L"ffmpeg_producer " << filename_ << L".";\r
152                 return str.str();\r
153         }\r
154                         \r
155         bool has_audio_;\r
156 \r
157         input_uptr                                                      input_;         \r
158 \r
159         video_decoder_uptr                                      video_decoder_;\r
160         video_transformer_uptr                          video_transformer_;\r
161         std::deque<frame_ptr>                           video_frame_channel_;\r
162         \r
163         audio_decoder_ptr                                       audio_decoder_;\r
164         std::deque<std::vector<short>>          audio_chunk_channel_;\r
165 \r
166         std::queue<frame_ptr>                           ouput_channel_;\r
167         \r
168         std::wstring                                            filename_;\r
169 \r
170         long                                                            underrun_count_;\r
171 \r
172         frame_ptr                                                       last_frame_;\r
173 };\r
174 \r
175 frame_producer_ptr create_ffmpeg_producer(const  std::vector<std::wstring>& params)\r
176 {       \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
179         \r
180         auto ext = std::find_if(extensions.begin(), extensions.end(), [&](const std::wstring& ex) -> bool\r
181                 {                                       \r
182                         return boost::filesystem::is_regular_file(boost::filesystem::wpath(filename).replace_extension(ex));\r
183                 });\r
184 \r
185         if(ext == extensions.end())\r
186                 return nullptr;\r
187 \r
188         return std::make_shared<ffmpeg_producer>(filename + L"." + *ext, params);\r
189 }\r
190 \r
191 }}}