]> 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 "../../frame/frame_format.h"\r
31 #include "../../../common/utility/find_file.h"\r
32 #include "../../../common/utility/memory.h"\r
33 #include "../../../common/utility/scope_exit.h"\r
34 #include "../../server.h"\r
35 \r
36 #include <tbb/mutex.h>\r
37 #include <tbb/parallel_invoke.h>\r
38 #include <tbb/task_group.h>\r
39 \r
40 #include <boost/algorithm/string/case_conv.hpp>\r
41 #include <boost/lexical_cast.hpp>\r
42 #include <boost/thread.hpp>\r
43 #include <boost/thread/once.hpp>\r
44 \r
45 using namespace boost::assign;\r
46 \r
47 namespace caspar { namespace core { namespace ffmpeg{\r
48         \r
49 struct ffmpeg_producer : public frame_producer\r
50 {\r
51 public:\r
52         ffmpeg_producer(const std::wstring& filename, const  std::vector<std::wstring>& params, const frame_format_desc& format_desc) \r
53                 : filename_(filename), format_desc_(format_desc)\r
54         {\r
55         if(!boost::filesystem::exists(filename))\r
56                 BOOST_THROW_EXCEPTION(file_not_found() <<  boost::errinfo_file_name(common::narrow(filename)));\r
57                 \r
58                 static boost::once_flag flag = BOOST_ONCE_INIT;\r
59                 boost::call_once(av_register_all, flag);        \r
60                                 \r
61                 input_.reset(new input(format_desc_));\r
62                 input_->set_loop(std::find(params.begin(), params.end(), L"LOOP") != params.end());\r
63                 input_->load(common::narrow(filename_));\r
64                 video_decoder_.reset(new video_decoder());\r
65                 video_transformer_.reset(new video_transformer());\r
66                 audio_decoder_.reset(new audio_decoder());\r
67                 has_audio_ = input_->get_audio_codec_context() != nullptr;\r
68 \r
69                 auto seek = std::find(params.begin(), params.end(), L"SEEK");\r
70                 if(seek != params.end() && ++seek != params.end())\r
71                 {\r
72                         if(!input_->seek(boost::lexical_cast<unsigned long long>(*seek)))\r
73                                 CASPAR_LOG(warning) << "Failed to seek file: " << filename_  << "to frame" << *seek;\r
74                 }\r
75 \r
76                 input_->start();\r
77         }\r
78                 \r
79         void initialize(const frame_factory_ptr& factory)\r
80         {\r
81                 video_transformer_->initialize(factory);\r
82         }\r
83                 \r
84         gpu_frame_ptr render_frame()\r
85         {\r
86                 while(ouput_channel_.empty() && !input_->is_eof())\r
87                 {                                                                               \r
88                         tbb::parallel_invoke(\r
89                         [&]\r
90                         { // Video Decoding and Scaling\r
91                                 auto video_packet = input_->get_video_packet();\r
92                                 if(video_packet)\r
93                                 {\r
94                                         video_packet = video_decoder_->execute(video_packet);\r
95                                         auto frame = video_transformer_->execute(video_packet)->frame;\r
96                                         video_frame_channel_.push_back(std::move(frame));       \r
97                                 }\r
98                         }, \r
99                         [&] \r
100                         { // Audio Decoding\r
101                                 auto audio_packet = input_->get_audio_packet();\r
102                                 if(audio_packet)\r
103                                 {\r
104                                         audio_decoder_->execute(audio_packet);\r
105                                         for(size_t n = 0; n < audio_packet->audio_chunks.size(); ++n)\r
106                                                 audio_chunk_channel_.push_back(std::move(audio_packet->audio_chunks[n]));\r
107                                 }\r
108                         });\r
109 \r
110                         while(!video_frame_channel_.empty() && (!audio_chunk_channel_.empty() || !has_audio_))\r
111                         {\r
112                                 if(has_audio_)\r
113                                 {\r
114                                         video_frame_channel_.front()->audio_data() = std::move(audio_chunk_channel_.front());\r
115                                         audio_chunk_channel_.pop_front();\r
116                                 }\r
117                                 \r
118                                 gpu_frame_ptr frame = video_frame_channel_.front();\r
119                                 video_frame_channel_.pop_front();\r
120                                 ouput_channel_.push(std::move(frame));\r
121                         }                               \r
122                 }\r
123 \r
124                 gpu_frame_ptr frame;\r
125                 if(!ouput_channel_.empty())\r
126                 {\r
127                         frame = ouput_channel_.front();\r
128                         ouput_channel_.pop();\r
129                 }\r
130                 return frame;\r
131         }\r
132 \r
133         const frame_format_desc& get_frame_format_desc() const { return format_desc_; }\r
134                 \r
135         bool has_audio_;\r
136 \r
137         // Filter 1 : Input\r
138         input_uptr                                                      input_;         \r
139 \r
140         // Filter 2 : Video Decoding and Scaling\r
141         video_decoder_uptr                                      video_decoder_;\r
142         video_transformer_uptr                          video_transformer_;\r
143         //std::deque<video_packet_ptr>          videoDecodedPacketChannel_;\r
144         std::deque<gpu_frame_ptr>                       video_frame_channel_;\r
145         \r
146         // Filter 3 : Audio Decoding\r
147         audio_decoder_uptr                                      audio_decoder_;\r
148         std::deque<std::vector<short>>          audio_chunk_channel_;\r
149 \r
150         // Filter 4 : Merge Video and Audio\r
151         std::queue<gpu_frame_ptr>                       ouput_channel_;\r
152         \r
153         std::wstring                                            filename_;\r
154         frame_format_desc                                       format_desc_;\r
155 };\r
156 \r
157 frame_producer_ptr create_ffmpeg_producer(const  std::vector<std::wstring>& params, const frame_format_desc& format_desc)\r
158 {\r
159         std::wstring filename = params[0];\r
160         std::wstring result_filename = common::find_file(server::media_folder() + filename, list_of(L"mpg")(L"avi")(L"mov")(L"dv")(L"wav")(L"mp3")(L"mp4")(L"f4v")(L"flv"));\r
161         if(result_filename.empty())\r
162                 return nullptr;\r
163 \r
164         return std::make_shared<ffmpeg_producer>(result_filename, params, format_desc);\r
165 }\r
166 \r
167 }}}