]> git.sesse.net Git - casparcg/blob - core/producer/ffmpeg/ffmpeg_producer.cpp
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches...
[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_deinterlacer.h"\r
29 #include "video/video_scaler.h"\r
30 \r
31 #include "../../frame/frame_format.h"\r
32 #include "../../../common/utility/find_file.h"\r
33 #include "../../server.h"\r
34 #include "../../../common/image/image.h"\r
35 #include "../../../common/utility/scope_exit.h"\r
36 \r
37 #include <tbb/mutex.h>\r
38 #include <tbb/parallel_invoke.h>\r
39 #include <tbb/task_group.h>\r
40 \r
41 #include <boost/algorithm/string/case_conv.hpp>\r
42 #include <boost/lexical_cast.hpp>\r
43 #include <boost/thread.hpp>\r
44 #include <boost/thread/once.hpp>\r
45 \r
46 using namespace boost::assign;\r
47 \r
48 namespace caspar{ namespace ffmpeg{\r
49         \r
50 struct ffmpeg_producer : public frame_producer\r
51 {\r
52 public:\r
53         static const size_t MAX_TOKENS = 5;\r
54         static const size_t MIN_BUFFER_SIZE = 2;\r
55         static const size_t DEFAULT_BUFFER_SIZE = 8;\r
56         static const size_t MAX_BUFFER_SIZE = 64;\r
57         static const size_t LOAD_TARGET_BUFFER_SIZE = 4;\r
58         static const size_t THREAD_TIMEOUT_MS = 1000;\r
59 \r
60         ffmpeg_producer(const std::wstring& filename, const  std::vector<std::wstring>& params, const frame_format_desc& format_desc) \r
61                 : filename_(filename), format_desc_(format_desc)\r
62         {\r
63         if(!boost::filesystem::exists(filename))\r
64                 BOOST_THROW_EXCEPTION(file_not_found() <<  boost::errinfo_file_name(common::narrow(filename)));\r
65                 \r
66                 static boost::once_flag flag = BOOST_ONCE_INIT;\r
67                 boost::call_once(av_register_all, flag);        \r
68                 \r
69                 input_.reset(new input(format_desc));\r
70                 input_->set_loop(std::find(params.begin(), params.end(), L"LOOP") != params.end());\r
71                 input_->load(common::narrow(filename_));\r
72 \r
73                 sound_channel_info_ptr snd_channel_info = input_->get_audio_codec_context() != nullptr ? \r
74                                 std::make_shared<sound_channel_info>\r
75                                 (\r
76                                         input_->get_audio_codec_context()->channels, \r
77                                         input_->get_audio_codec_context()->bits_per_coded_sample, \r
78                                         input_->get_audio_codec_context()->sample_rate\r
79                                 ) : nullptr;\r
80                 \r
81                 video_decoder_.reset(new video_decoder());\r
82                 video_scaler_.reset(new video_scaler());\r
83                 audio_decoder_.reset(new audio_decoder(snd_channel_info));\r
84                 has_audio_ = input_->get_audio_codec_context() != nullptr;\r
85         }\r
86                 \r
87         frame_ptr get_frame()\r
88         {\r
89                 while(ouput_channel_.empty() && !input_->is_eof())\r
90                 {                                                                               \r
91                         tbb::parallel_invoke(\r
92                         [&]\r
93                         { // Video Decoding and Scaling\r
94                                 auto video_packet = input_->get_video_packet();\r
95                                 if(video_packet)\r
96                                 {\r
97                                         video_packet = video_decoder_->execute(video_packet);\r
98                                         auto frame = video_scaler_->execute(video_packet)->frame;\r
99                                         video_frame_channel_.push_back(std::move(frame));       \r
100                                 }\r
101                         }, \r
102                         [&] \r
103                         { // Audio Decoding\r
104                                 auto audio_packet = input_->get_audio_packet();\r
105                                 if(audio_packet)\r
106                                 {\r
107                                         auto audio_chunks = audio_decoder_->execute(audio_packet);\r
108                                         audio_chunk_channel_.insert(audio_chunk_channel_.end(), audio_packet->audio_chunks.begin(), audio_packet->audio_chunks.end());\r
109                                 }\r
110                         });\r
111 \r
112                         while(!video_frame_channel_.empty() && (!audio_chunk_channel_.empty() || !has_audio_))\r
113                         {\r
114                                 if(has_audio_)\r
115                                 {\r
116                                         video_frame_channel_.front()->audio_data().push_back(audio_chunk_channel_.front());\r
117                                         audio_chunk_channel_.pop_front();\r
118                                 }\r
119                                 \r
120                                 frame_ptr frame = video_frame_channel_.front();\r
121                                 video_frame_channel_.pop_front();\r
122                                 ouput_channel_.push(std::move(frame));\r
123                         }                               \r
124                 }\r
125 \r
126                 frame_ptr frame;\r
127                 if(!ouput_channel_.empty())\r
128                 {\r
129                         frame = ouput_channel_.front();\r
130                         ouput_channel_.pop();\r
131                 }\r
132                 return frame;\r
133         }\r
134 \r
135         const frame_format_desc& get_frame_format_desc() const { return format_desc_; }\r
136                 \r
137         bool has_audio_;\r
138 \r
139         // Filter 1 : Input\r
140         input_uptr                                              input_;         \r
141 \r
142         // Filter 2 : Video Decoding and Scaling\r
143         video_decoder_uptr                              video_decoder_;\r
144         video_scaler_uptr                               video_scaler_;\r
145         //std::deque<video_packet_ptr>                                  videoDecodedPacketChannel_;\r
146         std::deque<frame_ptr>                   video_frame_channel_;\r
147         \r
148         // Filter 3 : Audio Decoding\r
149         audio_decoder_uptr                              audio_decoder_;\r
150         std::deque<audio_chunk_ptr>             audio_chunk_channel_;\r
151 \r
152         // Filter 4 : Merge Video and Audio\r
153         std::queue<frame_ptr>                   ouput_channel_;\r
154         \r
155         std::wstring                                    filename_;\r
156         frame_format_desc                       format_desc_;\r
157 };\r
158 \r
159 frame_producer_ptr create_ffmpeg_producer(const  std::vector<std::wstring>& params, const frame_format_desc& format_desc)\r
160 {\r
161         std::wstring filename = params[0];\r
162         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
163         if(result_filename.empty())\r
164                 return nullptr;\r
165 \r
166         return std::make_shared<ffmpeg_producer>(result_filename, params, format_desc);\r
167 }\r
168 \r
169 }}