]> 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 #include "input.h"\r
6 #include "audio/audio_decoder.h"\r
7 #include "video/video_decoder.h"\r
8 \r
9 #include "../../video_format.h"\r
10 #include "../../processor/draw_frame.h"\r
11 \r
12 #include <common/env.h>\r
13 \r
14 #include <tbb/parallel_invoke.h>\r
15 \r
16 #include <boost/regex.hpp>\r
17 \r
18 #include <deque>\r
19 \r
20 using namespace boost::assign;\r
21 \r
22 namespace caspar { namespace core { namespace ffmpeg{\r
23         \r
24 struct ffmpeg_producer : public frame_producer\r
25 {\r
26         input                                                           input_;                 \r
27         std::unique_ptr<audio_decoder>          audio_decoder_;\r
28         video_decoder                                           video_decoder_;\r
29 \r
30         std::deque<safe_ptr<write_frame>>       video_frame_channel_;   \r
31         std::deque<std::vector<short>>          audio_chunk_channel_;\r
32 \r
33         std::queue<safe_ptr<draw_frame>>        ouput_channel_;\r
34         \r
35         const std::wstring                                      filename_;\r
36         \r
37         safe_ptr<draw_frame>                            last_frame_;\r
38 \r
39         video_format_desc                                       format_desc_;\r
40 \r
41 public:\r
42         explicit ffmpeg_producer(const std::wstring& filename, bool loop, double start_time = 0, double end_time = -1.0) \r
43                 : filename_(filename)\r
44                 , last_frame_(draw_frame(draw_frame::empty()))\r
45                 , input_(filename, loop, start_time, end_time)\r
46                 , video_decoder_(input_.get_video_codec_context().get())                \r
47                 , audio_decoder_(input_.get_audio_codec_context().get() ? new audio_decoder(input_.get_audio_codec_context().get(), input_.fps()) : nullptr){}\r
48 \r
49         virtual void initialize(const safe_ptr<frame_processor_device>& frame_processor)\r
50         {\r
51                 format_desc_ = frame_processor->get_video_format_desc();\r
52                 video_decoder_.initialize(frame_processor);\r
53         }\r
54                 \r
55         virtual safe_ptr<draw_frame> receive()\r
56         {\r
57                 while(ouput_channel_.empty() && !input_.is_eof())\r
58                 {       \r
59                         aligned_buffer video_packet;\r
60                         if(video_frame_channel_.size() < 3)     \r
61                                 video_packet = input_.get_video_packet();               \r
62                         \r
63                         aligned_buffer audio_packet;\r
64                         if(audio_chunk_channel_.size() < 3)     \r
65                                 audio_packet = input_.get_audio_packet();               \r
66 \r
67                         tbb::parallel_invoke(\r
68                         [&]\r
69                         { // Video Decoding and Scaling\r
70                                 if(!video_packet.empty())\r
71                                 {\r
72                                         auto frame = video_decoder_.execute(video_packet);\r
73                                         video_frame_channel_.push_back(std::move(frame));       \r
74                                 }\r
75                         }, \r
76                         [&] \r
77                         { // Audio Decoding\r
78                                 if(!audio_packet.empty() && audio_decoder_)\r
79                                 {\r
80                                         try\r
81                                         {\r
82                                                 auto chunks = audio_decoder_->execute(audio_packet);\r
83                                                 audio_chunk_channel_.insert(audio_chunk_channel_.end(), chunks.begin(), chunks.end());\r
84                                         }\r
85                                         catch(...)\r
86                                         {\r
87                                                 CASPAR_LOG_CURRENT_EXCEPTION();\r
88                                                 audio_decoder_.reset();\r
89                                         }\r
90                                 }\r
91                         });\r
92 \r
93                         while(!video_frame_channel_.empty() && (!audio_chunk_channel_.empty() || !audio_decoder_))\r
94                         {\r
95                                 if(audio_decoder_) \r
96                                 {\r
97                                         video_frame_channel_.front()->audio_data() = std::move(audio_chunk_channel_.front());\r
98                                         audio_chunk_channel_.pop_front();\r
99                                 }\r
100                                                         \r
101                                 ouput_channel_.push(video_frame_channel_.front());\r
102                                 video_frame_channel_.pop_front();\r
103                         }                               \r
104 \r
105                         if(ouput_channel_.empty() && video_packet.empty() && audio_packet.empty())                      \r
106                                 return last_frame_;                     \r
107                 }\r
108 \r
109                 auto result = last_frame_;\r
110                 if(!ouput_channel_.empty())\r
111                 {\r
112                         result = std::move(ouput_channel_.front());\r
113                         last_frame_ = draw_frame(result);\r
114                         last_frame_->audio_volume(0.0); // last_frame should not have audio\r
115                         ouput_channel_.pop();\r
116                 }\r
117                 else if(input_.is_eof())\r
118                         return draw_frame::eof();\r
119 \r
120                 return result;\r
121         }\r
122 \r
123         virtual std::wstring print() const\r
124         {\r
125                 return L"ffmpeg[" + boost::filesystem::wpath(filename_).filename() + L"]";\r
126         }\r
127 };\r
128 \r
129 safe_ptr<frame_producer> create_ffmpeg_producer(const std::vector<std::wstring>& params)\r
130 {                       \r
131         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
132         std::wstring filename = env::media_folder() + L"\\" + params[0];\r
133         \r
134         auto ext = std::find_if(extensions.begin(), extensions.end(), [&](const std::wstring& ex) -> bool\r
135                 {                                       \r
136                         return boost::filesystem::is_regular_file(boost::filesystem::wpath(filename).replace_extension(ex));\r
137                 });\r
138 \r
139         if(ext == extensions.end())\r
140                 return frame_producer::empty();\r
141 \r
142         std::wstring path = filename + L"." + *ext;\r
143         bool loop = std::find(params.begin(), params.end(), L"LOOP") != params.end();\r
144         \r
145         static boost::wregex frame_range_expr(L".*\\[(?<START>\\d+\\.?\\d*)(-?(?<END>\\d+\\.?\\d*))?\\].*");\r
146 \r
147         double start_time = 0.0;\r
148         double end_time = -1.0;\r
149         \r
150         boost::wsmatch what;\r
151         if(std::find_if(params.begin(), params.end(), [&](const std::wstring& str){ return boost::regex_match(str, what, frame_range_expr);}) != params.end())\r
152         {\r
153                 try{start_time = boost::lexical_cast<double>(what["START"].str());}\r
154                 catch(boost::bad_lexical_cast&){}\r
155 \r
156                 try{end_time = boost::lexical_cast<double>(what["END"].str());}\r
157                 catch(boost::bad_lexical_cast&){}\r
158         }\r
159 \r
160         return make_safe<ffmpeg_producer>(path, loop, start_time, end_time);\r
161 }\r
162 \r
163 }}}