]> git.sesse.net Git - casparcg/blob - core/producer/transition/transition_producer.cpp
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches...
[casparcg] / core / producer / transition / transition_producer.cpp
1 /*\r
2 * copyright (c) 2010 Sveriges Television AB <info@casparcg.com>\r
3 *\r
4 *  This file is part of CasparCG.\r
5 *\r
6 *    CasparCG is free software: you can redistribute it and/or modify\r
7 *    it under the terms of the GNU General Public License as published by\r
8 *    the Free Software Foundation, either version 3 of the License, or\r
9 *    (at your option) any later version.\r
10 *\r
11 *    CasparCG is distributed in the hope that it will be useful,\r
12 *    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14 *    GNU General Public License for more details.\r
15 \r
16 *    You should have received a copy of the GNU General Public License\r
17 *    along with CasparCG.  If not, see <http://www.gnu.org/licenses/>.\r
18 *\r
19 */ \r
20 #include "../../stdafx.h"\r
21 \r
22 #include "transition_producer.h"\r
23 \r
24 #include "../../frame/frame_format.h"\r
25 \r
26 #include "../../../common/image/image.h"\r
27 #include "../../frame/system_frame.h"\r
28 #include "../../frame/audio_chunk.h"\r
29 #include "../../renderer/render_device.h"\r
30 \r
31 #include <boost/range/algorithm/copy.hpp>\r
32 \r
33 namespace caspar{       \r
34         \r
35 class empty_producer : public frame_producer\r
36 {\r
37 public:\r
38         explicit empty_producer(const frame_format_desc& format_desc) \r
39                 : format_desc_(format_desc), frame_(clear_frame(std::make_shared<system_frame>(format_desc_.size)))\r
40         {}      \r
41 \r
42         frame_ptr get_frame() { return frame_; }\r
43         const frame_format_desc& get_frame_format_desc() const { return format_desc_; }\r
44 private:\r
45         frame_format_desc format_desc_;\r
46         frame_ptr frame_;\r
47 };\r
48 \r
49 struct transition_producer::implementation : boost::noncopyable\r
50 {\r
51         implementation(const frame_producer_ptr& dest, const transition_info& info, const frame_format_desc& format_desc) \r
52                 : current_frame_(0), info_(info), border_color_(0), format_desc_(format_desc), \r
53                         empty_(std::make_shared<empty_producer>(format_desc)), source_(empty_), dest_(dest)\r
54         {\r
55                 if(!dest)\r
56                         BOOST_THROW_EXCEPTION(null_argument() << arg_name_info("dest"));\r
57         }\r
58                 \r
59         frame_producer_ptr get_following_producer() const\r
60         {\r
61                 return dest_;\r
62         }\r
63         \r
64         void set_leading_producer(const frame_producer_ptr& producer)\r
65         {\r
66                 source_ = producer != nullptr ? producer : empty_;\r
67         }\r
68                 \r
69         frame_ptr get_frame()\r
70         {\r
71                 if(++current_frame_ >= info_.duration)\r
72                         return nullptr;\r
73 \r
74                 return compose(get_producer_frame(dest_), get_producer_frame(source_));\r
75         }\r
76 \r
77         frame_ptr get_producer_frame(frame_producer_ptr& producer)\r
78         {\r
79                 assert(producer != nullptr);\r
80 \r
81                 frame_ptr frame;\r
82                 try\r
83                 {\r
84                         frame = producer->get_frame();\r
85                 }\r
86                 catch(...)\r
87                 {\r
88                         CASPAR_LOG_CURRENT_EXCEPTION();\r
89                         CASPAR_LOG(warning) << "Removed renderer from transition.";\r
90                 }\r
91 \r
92                 if(frame == nullptr && producer->get_following_producer() != nullptr)\r
93                 {\r
94                         auto following = producer->get_following_producer();\r
95                         following->set_leading_producer(producer);\r
96                         producer = following;\r
97                         return get_producer_frame(producer);\r
98                 }\r
99                 return frame;\r
100         }\r
101                         \r
102         frame_ptr compose(const frame_ptr& dest_frame, const frame_ptr& src_frame) \r
103         {               \r
104                 frame_ptr result_frame = dest_frame;            \r
105                 if(src_frame != nullptr && dest_frame != nullptr)\r
106                 {\r
107                         result_frame = std::make_shared<system_frame>(format_desc_.size);\r
108                         tbb::parallel_invoke(\r
109                         [&]\r
110                         {\r
111                                 GenerateFrame(result_frame->data(), src_frame->data(), dest_frame->data());\r
112                         },\r
113                         [&]\r
114                         {\r
115                                 float delta = static_cast<float>(current_frame_)/static_cast<float>(info_.duration);\r
116                                 set_frame_volume(dest_frame, delta*100.0f);\r
117                                 set_frame_volume(src_frame, (1.0f-delta)*100.0f);               \r
118 \r
119                                 boost::range::copy(src_frame->audio_data(), std::back_inserter(result_frame->audio_data()));                            \r
120                                 boost::range::copy(dest_frame->audio_data(), std::back_inserter(result_frame->audio_data()));;\r
121                         });\r
122                 }\r
123                 return result_frame;\r
124         }\r
125         \r
126         void GenerateFrame(unsigned char* pResultData, const unsigned char* pSourceData, const unsigned char* pDestData)\r
127         {\r
128                 if(info_.type == transition_type::cut)\r
129                 {\r
130                         common::image::copy(pResultData, pSourceData, format_desc_.size);\r
131                         return;\r
132                 }\r
133 \r
134                 if(current_frame_ >= info_.duration)                    \r
135                 {\r
136                         common::image::copy(pResultData, pDestData, format_desc_.size);\r
137                         return;\r
138                 }\r
139 \r
140                 if(info_.type == transition_type::mix)\r
141                 {\r
142                         common::image::lerp(pResultData, pSourceData, pDestData, 1.0f-static_cast<float>(current_frame_)/static_cast<float>(info_.duration), format_desc_.size);\r
143                         return;\r
144                 }\r
145 \r
146                 size_t totalWidth = format_desc_.width + info_.border_width;\r
147                         \r
148                 float fStep   = totalWidth / static_cast<float>(info_.duration);\r
149                 float fOffset = fStep * static_cast<float>(current_frame_);\r
150 \r
151                 size_t halfStep = static_cast<size_t>(fStep/2.0);\r
152                 size_t offset   = static_cast<size_t>(fOffset+0.5f);\r
153                         \r
154                 //read source to buffer\r
155                 for(size_t row = 0, even = 0; row < format_desc_.height; ++row, even ^= 1)\r
156                 {\r
157                         size_t fieldCorrectedOffset = offset + (halfStep*even);\r
158                         if(fieldCorrectedOffset < format_desc_.width)\r
159                         {\r
160                                 if(info_.direction != transition_direction::from_left)\r
161                                 {\r
162                                         if(info_.type == transition_type::push)\r
163                                                 memcpy(&(pResultData[4*row*format_desc_.width]), &(pSourceData[4*(row*format_desc_.width+fieldCorrectedOffset)]), (format_desc_.width-fieldCorrectedOffset)*4);\r
164                                         else    //Slide | Wipe\r
165                                                 memcpy(&(pResultData[4*row*format_desc_.width]), &(pSourceData[4*row*format_desc_.width]), (format_desc_.width-fieldCorrectedOffset)*4);\r
166                                 }\r
167                                 else // if (direction == LEFT)\r
168                                 {                               \r
169                                         if(info_.type == transition_type::push)\r
170                                                 memcpy(&(pResultData[4*(row*format_desc_.width+fieldCorrectedOffset)]), &(pSourceData[4*(row*format_desc_.width)]), (format_desc_.width-fieldCorrectedOffset)*4);\r
171                                         else    //slide eller wipe\r
172                                                 memcpy(&(pResultData[4*(row*format_desc_.width+fieldCorrectedOffset)]), &(pSourceData[4*(row*format_desc_.width+fieldCorrectedOffset)]), (format_desc_.width-fieldCorrectedOffset)*4);\r
173                                 }\r
174                         }\r
175                 }\r
176 \r
177                 //write border to buffer\r
178                 if(info_.border_width > 0)\r
179                 {\r
180                         for(size_t row = 0, even = 0; row < format_desc_.height; ++row, even ^= 1)\r
181                         {\r
182                                 size_t fieldCorrectedOffset = offset + (halfStep*even);\r
183                                 size_t length = info_.border_width;\r
184                                 size_t start = 0;\r
185 \r
186                                 if(info_.direction != transition_direction::from_left)\r
187                                 {\r
188                                         if(fieldCorrectedOffset > format_desc_.width)\r
189                                         {\r
190                                                 length -= fieldCorrectedOffset-format_desc_.width;\r
191                                                 start += fieldCorrectedOffset-format_desc_.width;\r
192                                                 fieldCorrectedOffset = format_desc_.width;\r
193                                         }\r
194                                         else if(fieldCorrectedOffset < length)\r
195                                         {\r
196                                                 length = fieldCorrectedOffset;\r
197                                         }\r
198 \r
199                                         for(size_t i = 0; i < length; ++i)\r
200                                                 memcpy(&(pResultData[4*(row*format_desc_.width+format_desc_.width-fieldCorrectedOffset+i)]), &border_color_, 4);\r
201                                                 \r
202                                 }\r
203                                 else // if (direction == LEFT)\r
204                                 {\r
205                                         if(fieldCorrectedOffset > format_desc_.width)\r
206                                         {\r
207                                                 length -= fieldCorrectedOffset-format_desc_.width;\r
208                                                 start = 0;\r
209                                                 fieldCorrectedOffset -= info_.border_width-length;\r
210                                         }\r
211                                         else if(fieldCorrectedOffset < length)\r
212                                         {\r
213                                                 length = fieldCorrectedOffset;\r
214                                                 start = info_.border_width-fieldCorrectedOffset;\r
215                                         }\r
216 \r
217                                         for(size_t i = 0; i < length; ++i)\r
218                                                 memcpy(&(pResultData[4*(row*format_desc_.width+fieldCorrectedOffset-length+i)]), &border_color_, 4);                                            \r
219                                 }\r
220 \r
221                         }\r
222                 }\r
223 \r
224                 //read dest to buffer\r
225                 offset -= info_.border_width;\r
226                 if(offset > 0)\r
227                 {\r
228                         for(size_t row = 0, even = 0; row < format_desc_.height; ++row, even ^= 1)\r
229                         {\r
230                                 int fieldCorrectedOffset = offset + (halfStep*even);\r
231 \r
232                                 if(info_.direction != transition_direction::from_left)\r
233                                 {\r
234                                         if(info_.type == transition_type::wipe)\r
235                                                 memcpy(&(pResultData[4*(row*format_desc_.width+format_desc_.width-fieldCorrectedOffset)]), &(pDestData[4*(row*format_desc_.width+format_desc_.width-fieldCorrectedOffset)]), fieldCorrectedOffset*4);\r
236                                         else\r
237                                                 memcpy(&(pResultData[4*(row*format_desc_.width+format_desc_.width-fieldCorrectedOffset)]), &(pDestData[4*row*format_desc_.width]), fieldCorrectedOffset*4);\r
238                                 }\r
239                                 else // if (direction == LEFT)\r
240                                 {                               \r
241                                         if(info_.type == transition_type::wipe)\r
242                                                 memcpy(&(pResultData[4*(row*format_desc_.width)]), &(pDestData[4*(row*format_desc_.width)]), fieldCorrectedOffset*4);\r
243                                         else\r
244                                                 memcpy(&(pResultData[4*(row*format_desc_.width)]), &(pDestData[4*(row*format_desc_.width+format_desc_.width-fieldCorrectedOffset)]), fieldCorrectedOffset*4);   \r
245                                 }\r
246                         }\r
247                 }\r
248         }\r
249 \r
250         const frame_format_desc format_desc_;\r
251 \r
252         frame_producer_ptr              empty_;\r
253         frame_producer_ptr              source_;\r
254         frame_producer_ptr              dest_;\r
255         \r
256         unsigned short                  current_frame_;\r
257         \r
258         const transition_info   info_;\r
259         const unsigned long             border_color_;\r
260 };\r
261 \r
262 transition_producer::transition_producer(const frame_producer_ptr& dest, const transition_info& info, const frame_format_desc& format_desc) \r
263         : impl_(new implementation(dest, info, format_desc)){}\r
264 frame_ptr transition_producer::get_frame(){return impl_->get_frame();}\r
265 frame_producer_ptr transition_producer::get_following_producer() const{return impl_->get_following_producer();}\r
266 void transition_producer::set_leading_producer(const frame_producer_ptr& producer) { impl_->set_leading_producer(producer); }\r
267 const frame_format_desc& transition_producer::get_frame_format_desc() const { return impl_->format_desc_; } \r
268 \r
269 }