]> git.sesse.net Git - casparcg/blob - core/producer/ffmpeg/ffmpeg_producer.cpp
c9f66f14b2d195a9e348c83112ab23614448359e
[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 "../../video/video_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) \r
53                 : filename_(filename)\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());\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(input_->get_video_codec_context().get()));\r
65                 video_transformer_.reset(new video_transformer(input_->get_video_codec_context().get()));\r
66                 audio_decoder_.reset(new audio_decoder(input_->get_audio_codec_context().get()));\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_processor_device_ptr& frame_processor)\r
80         {\r
81                 video_transformer_->initialize(frame_processor);\r
82         }\r
83                 \r
84         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.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                                 auto audio_packet = input_->get_audio_packet();\r
102                                 if(!audio_packet.empty())\r
103                                 {\r
104                                         auto chunks = audio_decoder_->execute(audio_packet);\r
105                                         audio_chunk_channel_.insert(audio_chunk_channel_.end(), chunks.begin(), chunks.end());\r
106                                 }\r
107                         });\r
108 \r
109                         while(!video_frame_channel_.empty() && (!audio_chunk_channel_.empty() || !has_audio_))\r
110                         {\r
111                                 if(has_audio_)\r
112                                 {\r
113                                         video_frame_channel_.front()->audio_data() = std::move(audio_chunk_channel_.front());\r
114                                         audio_chunk_channel_.pop_front();\r
115                                 }\r
116                                 \r
117                                 frame_ptr frame = video_frame_channel_.front();\r
118                                 video_frame_channel_.pop_front();\r
119                                 ouput_channel_.push(std::move(frame));\r
120                         }                               \r
121                 }\r
122 \r
123                 frame_ptr frame;\r
124                 if(!ouput_channel_.empty())\r
125                 {\r
126                         frame = ouput_channel_.front();\r
127                         ouput_channel_.pop();\r
128                 }\r
129                 return frame;\r
130         }\r
131                         \r
132         bool has_audio_;\r
133 \r
134         // Filter 1 : Input\r
135         input_uptr                                                      input_;         \r
136 \r
137         // Filter 2 : Video Decoding and Scaling\r
138         video_decoder_uptr                                      video_decoder_;\r
139         video_transformer_uptr                          video_transformer_;\r
140         //std::deque<video_packet_ptr>          videoDecodedPacketChannel_;\r
141         std::deque<frame_ptr>                                   video_frame_channel_;\r
142         \r
143         // Filter 3 : Audio Decoding\r
144         audio_decoder_ptr                                       audio_decoder_;\r
145         std::deque<std::vector<short>>          audio_chunk_channel_;\r
146 \r
147         // Filter 4 : Merge Video and Audio\r
148         std::queue<frame_ptr>                   ouput_channel_;\r
149         \r
150         std::wstring                                            filename_;\r
151 };\r
152 \r
153 frame_producer_ptr create_ffmpeg_producer(const  std::vector<std::wstring>& params)\r
154 {\r
155         std::wstring filename = params[0];\r
156         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
157         if(result_filename.empty())\r
158                 return nullptr;\r
159 \r
160         return std::make_shared<ffmpeg_producer>(result_filename, params);\r
161 }\r
162 \r
163 }}}