]> git.sesse.net Git - casparcg/blob - core/producer/ffmpeg/ffmpeg_producer.cpp
2.0.0.2: Decoupled producer, mixer and consumer. Could potentially be moved into...
[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 "../../mixer/frame/draw_frame.h"\r
11 #include "../../mixer/audio/audio_mixer.h"\r
12 \r
13 #include <common/env.h>\r
14 \r
15 #include <tbb/parallel_invoke.h>\r
16 \r
17 #include <deque>\r
18 \r
19 namespace caspar { namespace core { namespace ffmpeg{\r
20         \r
21 struct ffmpeg_producer : public frame_producer\r
22 {\r
23         input                                                           input_;                 \r
24         std::unique_ptr<audio_decoder>          audio_decoder_;\r
25         video_decoder                                           video_decoder_;\r
26 \r
27         std::deque<safe_ptr<write_frame>>       video_frame_channel_;   \r
28         std::deque<std::vector<short>>          audio_chunk_channel_;\r
29 \r
30         std::queue<safe_ptr<draw_frame>>        ouput_channel_;\r
31         \r
32         const std::wstring                                      filename_;\r
33         \r
34         safe_ptr<draw_frame>                            last_frame_;\r
35 \r
36         video_format_desc                                       format_desc_;\r
37 \r
38 public:\r
39         explicit ffmpeg_producer(const std::wstring& filename, bool loop) \r
40                 : filename_(filename)\r
41                 , last_frame_(draw_frame(draw_frame::empty()))\r
42                 , input_(filename, loop)\r
43                 , video_decoder_(input_.get_video_codec_context().get())                \r
44                 , audio_decoder_(input_.get_audio_codec_context().get() ? new audio_decoder(input_.get_audio_codec_context().get(), input_.fps()) : nullptr){}\r
45 \r
46         virtual void initialize(const safe_ptr<frame_factory>& frame_factory)\r
47         {\r
48                 format_desc_ = frame_factory->get_video_format_desc();\r
49                 video_decoder_.initialize(frame_factory);\r
50         }\r
51                 \r
52         virtual safe_ptr<draw_frame> receive()\r
53         {\r
54                 while(ouput_channel_.empty() && !input_.is_eof())\r
55                 {       \r
56                         aligned_buffer video_packet;\r
57                         if(video_frame_channel_.size() < 3)     \r
58                                 video_packet = input_.get_video_packet();               \r
59                         \r
60                         aligned_buffer audio_packet;\r
61                         if(audio_chunk_channel_.size() < 3)     \r
62                                 audio_packet = input_.get_audio_packet();               \r
63 \r
64                         tbb::parallel_invoke(\r
65                         [&]\r
66                         { // Video Decoding and Scaling\r
67                                 if(!video_packet.empty())\r
68                                 {\r
69                                         auto frame = video_decoder_.execute(video_packet);\r
70                                         video_frame_channel_.push_back(std::move(frame));\r
71                                 }\r
72                         }, \r
73                         [&] \r
74                         { // Audio Decoding\r
75                                 if(!audio_packet.empty() && audio_decoder_)\r
76                                 {\r
77                                         try\r
78                                         {\r
79                                                 auto chunks = audio_decoder_->execute(audio_packet);\r
80                                                 audio_chunk_channel_.insert(audio_chunk_channel_.end(), chunks.begin(), chunks.end());\r
81                                         }\r
82                                         catch(...)\r
83                                         {\r
84                                                 CASPAR_LOG_CURRENT_EXCEPTION();\r
85                                                 audio_decoder_.reset();\r
86                                         }\r
87                                 }\r
88                         });\r
89 \r
90                         while(!video_frame_channel_.empty() && (!audio_chunk_channel_.empty() || !audio_decoder_))\r
91                         {\r
92                                 if(audio_decoder_) \r
93                                 {\r
94                                         video_frame_channel_.front()->audio_data() = std::move(audio_chunk_channel_.front());\r
95                                         audio_chunk_channel_.pop_front();\r
96                                 }\r
97                                                         \r
98                                 ouput_channel_.push(std::move(video_frame_channel_.front()));\r
99                                 video_frame_channel_.pop_front();\r
100                         }                               \r
101 \r
102                         if(ouput_channel_.empty() && video_packet.empty() && audio_packet.empty())                      \r
103                                 return last_frame_;                     \r
104                 }\r
105 \r
106                 auto result = last_frame_;\r
107                 if(!ouput_channel_.empty())\r
108                 {\r
109                         result = std::move(ouput_channel_.front());\r
110                         last_frame_ = draw_frame(result);\r
111                         last_frame_->get_audio_transform().gain = 0.0; // last_frame should not have audio\r
112                         ouput_channel_.pop();\r
113                 }\r
114                 else if(input_.is_eof())\r
115                         return draw_frame::eof();\r
116 \r
117                 return result;\r
118         }\r
119 \r
120         virtual std::wstring print() const\r
121         {\r
122                 return L"ffmpeg[" + boost::filesystem::wpath(filename_).filename() + L"]";\r
123         }\r
124 };\r
125 \r
126 safe_ptr<frame_producer> create_ffmpeg_producer(const std::vector<std::wstring>& params)\r
127 {                       \r
128         static const std::vector<std::wstring> extensions = boost::assign::list_of(L"mpg")(L"avi")(L"mov")(L"dv")(L"wav")(L"mp3")(L"mp4")(L"f4v")(L"flv");\r
129         std::wstring filename = env::media_folder() + L"\\" + params[0];\r
130         \r
131         auto ext = std::find_if(extensions.begin(), extensions.end(), [&](const std::wstring& ex) -> bool\r
132                 {                                       \r
133                         return boost::filesystem::is_regular_file(boost::filesystem::wpath(filename).replace_extension(ex));\r
134                 });\r
135 \r
136         if(ext == extensions.end())\r
137                 return frame_producer::empty();\r
138 \r
139         std::wstring path = filename + L"." + *ext;\r
140         bool loop = std::find(params.begin(), params.end(), L"LOOP") != params.end();\r
141         \r
142         return make_safe<ffmpeg_producer>(path, loop);\r
143 }\r
144 \r
145 }}}