]> git.sesse.net Git - casparcg/blob - server/producers/targascroll/TargaScrollProducer.cpp
1.8.1:
[casparcg] / server / producers / targascroll / TargaScrollProducer.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  \r
21 #include "..\..\StdAfx.h"\r
22 \r
23 #include "TargaScrollProducer.h"\r
24 #include "..\Targa\TargaManager.h"\r
25 #include "..\..\MediaProducer.h"\r
26 #include "..\..\FileInfo.h"\r
27 #include "..\..\utils\FileInputStream.h"\r
28 #include "..\..\utils\PixmapData.h"\r
29 \r
30 #include <boost/lexical_cast.hpp>\r
31 \r
32 namespace caspar {\r
33 using namespace utils;\r
34 \r
35 int TargaScrollMediaProducer::DEFAULT_SPEED = 4;\r
36 \r
37 TargaScrollMediaProducer::TargaScrollMediaProducer() : initializeEvent_(FALSE, FALSE)\r
38 {\r
39 }\r
40 \r
41 TargaScrollMediaProducer::~TargaScrollMediaProducer()\r
42 {\r
43         this->workerThread.Stop();\r
44 }\r
45 \r
46 bool TargaScrollMediaProducer::Load(const tstring& filename)\r
47 {\r
48         if (filename.length() > 0)\r
49         {\r
50                 tstring::size_type pos = filename.find_last_of(TEXT('_'));\r
51                 if(pos != tstring::npos && (pos+1) < filename.size()) {\r
52                         tstring speedStr = filename.substr(pos + 1);\r
53                         pos = speedStr.find_first_of(TEXT('.'));\r
54                         if(pos != tstring::npos)\r
55                                 speedStr = speedStr.substr(0, pos);             \r
56 \r
57                         try\r
58                         {\r
59                                 speed = boost::lexical_cast<int, tstring>(speedStr);\r
60                         }\r
61                         catch(...)\r
62                         {\r
63                                 speed = DEFAULT_SPEED;\r
64                         }\r
65                 }\r
66 \r
67                 utils::InputStreamPtr pTgaFile(new utils::FileInputStream(filename));\r
68                 if (pTgaFile->Open())\r
69                         return TargaManager::Load(pTgaFile, this->pImage);\r
70         }\r
71 \r
72         return false;\r
73 }\r
74 \r
75 IMediaController* TargaScrollMediaProducer::QueryController(const tstring& id) {\r
76         if(id == TEXT("FrameController"))\r
77                 return this;\r
78         \r
79         return 0;\r
80 }\r
81 \r
82 bool TargaScrollMediaProducer::Initialize(FrameManagerPtr pFrameManager) {\r
83         if(pFrameManager != this->pFrameManager_) {\r
84                 if(pFrameManager == 0)\r
85                         return false;\r
86 \r
87                 if(!workerThread.IsRunning()) {\r
88                         pFrameManager_ = pFrameManager;\r
89                         return workerThread.Start(this);\r
90                 }\r
91                 else\r
92                 {\r
93                         {\r
94                                 Lock lock(*this);\r
95 \r
96                                 if(pFrameManager_->GetFrameFormatDescription().width != pFrameManager->GetFrameFormatDescription().width || pFrameManager_->GetFrameFormatDescription().height != pFrameManager->GetFrameFormatDescription().height) {\r
97                                         return false;\r
98                                 }\r
99 \r
100                                 pTempFrameManager_ = pFrameManager;\r
101                         }\r
102 \r
103                         initializeEvent_.Set();\r
104                 }\r
105         }\r
106 \r
107         return true;\r
108 }\r
109 \r
110 \r
111 void TargaScrollMediaProducer::PadImageToFrameFormat()\r
112 {\r
113         const FrameFormatDescription& formatDescription = pFrameManager_->GetFrameFormatDescription();\r
114 \r
115         const unsigned int PIXMAP_WIDTH = max(this->pImage->width, formatDescription.width);\r
116         const unsigned int PIXMAP_HEIGHT = max(this->pImage->height, formatDescription.height);\r
117 \r
118         utils::PixmapDataPtr pNewImage(new utils::PixmapData(PIXMAP_WIDTH, PIXMAP_HEIGHT, this->pImage->bpp));\r
119 \r
120         unsigned char* pNewImageData = pNewImage->GetDataPtr();\r
121         unsigned char* pImageData = this->pImage->GetDataPtr();\r
122 \r
123         memset(pNewImageData, 0, pNewImage->width * pNewImage->height * pNewImage->bpp);\r
124 \r
125         for (int i = 0; i < this->pImage->height; ++i)\r
126                 memcpy(&pNewImageData[i* pNewImage->width * pNewImage->bpp], &pImageData[i* this->pImage->width * this->pImage->bpp], this->pImage->width * this->pImage->bpp);\r
127 \r
128         this->pImage = pNewImage;\r
129 }\r
130 \r
131 FramePtr TargaScrollMediaProducer::FillVideoFrame(FramePtr pFrame)\r
132 {\r
133         const FrameFormatDescription& formatDescription = pFrameManager_->GetFrameFormatDescription();\r
134 \r
135         const short deltaX = this->direction == DirectionFlag::ScrollLeft ? this->speed : -this->speed;\r
136         const short deltaY = this->direction == DirectionFlag::ScrollUp ? this->speed : -this->speed;\r
137 \r
138         unsigned char* pFrameData = pFrame->GetDataPtr();\r
139         unsigned char* pImageData = this->pImage->GetDataPtr();\r
140         \r
141         bool isFirstFrame = false, isLastFrame = false;\r
142         if (this->direction == DirectionFlag::ScrollUp || this->direction == DirectionFlag::ScrollDown)\r
143         {\r
144                 for (int i = 0; i < formatDescription.height; ++i)\r
145                 {\r
146                         int srcRow = i + this->offset; // Assume progressive.\r
147                         if (formatDescription.mode == Interlaced)\r
148                         {\r
149                                 const int nextOffset = this->offset + (deltaY * 2);\r
150                                 isFirstFrame = this->offset == 0 || this->offset == (this->pImage->height - formatDescription.height);\r
151                                 isLastFrame = nextOffset <= 0 || nextOffset >= (this->pImage->height - formatDescription.height);\r
152                                 if (!isFirstFrame && !isLastFrame)\r
153                                         srcRow = (i % 2 == 0) ? i + this->offset : min(i + this->offset + deltaY, this->pImage->height);\r
154                         }\r
155 \r
156                         int dstInxex = i * formatDescription.width * this->pImage->bpp;\r
157                         int srcIndex = srcRow * formatDescription.width * this->pImage->bpp;\r
158                         int size = formatDescription.width * this->pImage->bpp;\r
159 \r
160                         memcpy(&pFrameData[dstInxex], &pImageData[srcIndex], size);     \r
161                 }\r
162 \r
163                 if (formatDescription.mode == Interlaced && !isFirstFrame && !isLastFrame)\r
164                         this->offset += deltaY * 2;\r
165                 else\r
166                         this->offset += deltaY;\r
167         }\r
168         else\r
169         {\r
170                 for (int i = 0; i < formatDescription.height; ++i)\r
171                 {\r
172                         int correctOffset = this->offset; // Assume progressive.\r
173                         if (formatDescription.mode == Interlaced)\r
174                         {\r
175                                 const int nextOffset = this->offset + (deltaX * 2); // Next offset.\r
176                                 isFirstFrame = this->offset == 0 || this->offset == (this->pImage->width - formatDescription.width);\r
177                                 isLastFrame = nextOffset <= 0 || nextOffset >= (this->pImage->width - formatDescription.width);\r
178                                 if (!isFirstFrame && !isLastFrame)\r
179                                         correctOffset = (i % 2 == 0) ? this->offset: this->offset + deltaX;\r
180                         }\r
181 \r
182                         int dstIndex = i * formatDescription.width * this->pImage->bpp;\r
183                         int srcIndex = (i * this->pImage->width + correctOffset) * this->pImage->bpp;\r
184                         \r
185                         int stopOffset = min(correctOffset + formatDescription .width, this->pImage->width);\r
186                         int size = (stopOffset - correctOffset) * this->pImage->bpp;\r
187 \r
188                         memcpy(&pFrameData[dstIndex], &pImageData[srcIndex], size);\r
189                 }\r
190 \r
191                 if (formatDescription.mode == Interlaced && !isFirstFrame && !isLastFrame)\r
192                         this->offset += deltaX * 2;\r
193                 else\r
194                         this->offset += deltaX;\r
195         }\r
196 \r
197         return pFrame;\r
198 }\r
199 \r
200 void TargaScrollMediaProducer::Run(HANDLE stopEvent)\r
201 {\r
202         LOG << LogLevel::Verbose << TEXT("Targa scroll thread started");\r
203 \r
204         const short waitHandleCount = 3;\r
205         HANDLE waitHandles[waitHandleCount] = { stopEvent, initializeEvent_, this->frameBuffer.GetWriteWaitHandle() };\r
206 \r
207         int formatWidth = 0;\r
208         int formatHeight = 0;\r
209         {\r
210                 const FrameFormatDescription& formatDescription = pFrameManager_->GetFrameFormatDescription();\r
211                 formatWidth = formatDescription.width;\r
212                 formatHeight = formatDescription.height;\r
213 \r
214                 //determine whether to scroll horizontally or vertically\r
215                 if((this->pImage->width - formatWidth) > (pImage->height - formatHeight))\r
216                         direction = (speed < 0) ? DirectionFlag::ScrollRight : DirectionFlag::ScrollLeft;\r
217                 else\r
218                         direction = (speed < 0) ? DirectionFlag::ScrollDown : DirectionFlag::ScrollUp;\r
219 \r
220                 this->speed = abs(speed / formatDescription.fps);\r
221                 this->offset = 0;\r
222                 if (this->direction == DirectionFlag::ScrollDown)\r
223                         this->offset = this->pImage->height - formatHeight;\r
224                 else if (this->direction == DirectionFlag::ScrollRight)\r
225                         this->offset = this->pImage->width - formatWidth;\r
226         }\r
227 \r
228 \r
229         if (formatWidth > this->pImage->width || formatHeight > this->pImage->height)\r
230                 PadImageToFrameFormat();\r
231 \r
232         bool quitLoop = false;\r
233         while (!quitLoop)\r
234         {\r
235                 HRESULT waitResult = WaitForMultipleObjects(waitHandleCount, waitHandles, FALSE, 1000);\r
236                 switch(waitResult)\r
237                 {\r
238                         case WAIT_OBJECT_0 + 0:         // Stop.\r
239                         case WAIT_FAILED:                       // Wait failiure.\r
240                                 quitLoop = true;\r
241                                 continue;\r
242                         case WAIT_TIMEOUT:                      // Nothing has happened.\r
243                                 continue;\r
244                         case WAIT_OBJECT_0 + 1:         //initialize\r
245                                 {\r
246                                         Lock lock(*this);\r
247                                         pFrameManager_ = pTempFrameManager_;\r
248                                         pTempFrameManager_.reset();\r
249                                 }\r
250                                 break;\r
251 \r
252                         case WAIT_OBJECT_0 + 2:         // Framebuffer is ready to be filled.\r
253                         {\r
254                                 // Render next frame.\r
255                                 FramePtr pFrame = pFrameManager_->CreateFrame();\r
256                                 pFrame = FillVideoFrame(pFrame);\r
257                                 this->frameBuffer.push_back(pFrame);\r
258 \r
259                                 // Should we stop scrolling?\r
260                                 if ((this->direction == DirectionFlag::ScrollDown || this->direction == DirectionFlag::ScrollRight) && this->offset <= 0)\r
261                                         quitLoop = true;\r
262                                 else if (this->direction == DirectionFlag::ScrollUp && this->offset >= (this->pImage->height - formatHeight))\r
263                                         quitLoop = true;\r
264                                 else if (this->direction == DirectionFlag::ScrollLeft && this->offset >= (this->pImage->width - formatWidth))\r
265                                         quitLoop = true;\r
266                         }\r
267                 }\r
268         }\r
269         \r
270         // Render a null frame to indicate EOF.\r
271         FramePtr pNullFrame;\r
272         this->frameBuffer.push_back(pNullFrame);\r
273 \r
274         LOG << LogLevel::Verbose << TEXT("Targa scroll thread ended");\r
275 }\r
276 \r
277 bool TargaScrollMediaProducer::OnUnhandledException(const std::exception& ex) throw()\r
278 {\r
279         try\r
280         {\r
281                 FramePtr pNullFrame;\r
282                 this->frameBuffer.push_back(pNullFrame);\r
283 \r
284                 LOG << LogLevel::Critical << TEXT("UNHANDLED EXCEPTION in targa scroll thread. Message: ") << ex.what();\r
285         }\r
286         catch (...)\r
287         {\r
288         }\r
289 \r
290         return false;\r
291 }\r
292 \r
293 }