2 * copyright (c) 2010 Sveriges Television AB <info@casparcg.com>
\r
4 * This file is part of CasparCG.
\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
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
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
21 #include "..\..\StdAfx.h"
\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
30 #include <boost/lexical_cast.hpp>
\r
33 using namespace utils;
\r
35 int TargaScrollMediaProducer::DEFAULT_SPEED = 4;
\r
37 TargaScrollMediaProducer::TargaScrollMediaProducer() : initializeEvent_(FALSE, FALSE)
\r
41 TargaScrollMediaProducer::~TargaScrollMediaProducer()
\r
43 this->workerThread.Stop();
\r
46 bool TargaScrollMediaProducer::Load(const tstring& filename)
\r
48 if (filename.length() > 0)
\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
59 speed = boost::lexical_cast<int, tstring>(speedStr);
\r
63 speed = DEFAULT_SPEED;
\r
67 utils::InputStreamPtr pTgaFile(new utils::FileInputStream(filename));
\r
68 if (pTgaFile->Open())
\r
69 return TargaManager::Load(pTgaFile, this->pImage);
\r
75 IMediaController* TargaScrollMediaProducer::QueryController(const tstring& id) {
\r
76 if(id == TEXT("FrameController"))
\r
82 bool TargaScrollMediaProducer::Initialize(FrameManagerPtr pFrameManager) {
\r
83 if(pFrameManager != this->pFrameManager_) {
\r
84 if(pFrameManager == 0)
\r
87 if(!workerThread.IsRunning()) {
\r
88 pFrameManager_ = pFrameManager;
\r
89 return workerThread.Start(this);
\r
96 if(pFrameManager_->GetFrameFormatDescription().width != pFrameManager->GetFrameFormatDescription().width || pFrameManager_->GetFrameFormatDescription().height != pFrameManager->GetFrameFormatDescription().height) {
\r
100 pTempFrameManager_ = pFrameManager;
\r
103 initializeEvent_.Set();
\r
111 void TargaScrollMediaProducer::PadImageToFrameFormat()
\r
113 const FrameFormatDescription& formatDescription = pFrameManager_->GetFrameFormatDescription();
\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
118 utils::PixmapDataPtr pNewImage(new utils::PixmapData(PIXMAP_WIDTH, PIXMAP_HEIGHT, this->pImage->bpp));
\r
120 unsigned char* pNewImageData = pNewImage->GetDataPtr();
\r
121 unsigned char* pImageData = this->pImage->GetDataPtr();
\r
123 memset(pNewImageData, 0, pNewImage->width * pNewImage->height * pNewImage->bpp);
\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
128 this->pImage = pNewImage;
\r
131 FramePtr TargaScrollMediaProducer::FillVideoFrame(FramePtr pFrame)
\r
133 const FrameFormatDescription& formatDescription = pFrameManager_->GetFrameFormatDescription();
\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
138 unsigned char* pFrameData = pFrame->GetDataPtr();
\r
139 unsigned char* pImageData = this->pImage->GetDataPtr();
\r
141 bool isFirstFrame = false, isLastFrame = false;
\r
142 if (this->direction == DirectionFlag::ScrollUp || this->direction == DirectionFlag::ScrollDown)
\r
144 for (int i = 0; i < formatDescription.height; ++i)
\r
146 int srcRow = i + this->offset; // Assume progressive.
\r
147 if (formatDescription.mode == Interlaced)
\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
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
160 memcpy(&pFrameData[dstInxex], &pImageData[srcIndex], size);
\r
163 if (formatDescription.mode == Interlaced && !isFirstFrame && !isLastFrame)
\r
164 this->offset += deltaY * 2;
\r
166 this->offset += deltaY;
\r
170 for (int i = 0; i < formatDescription.height; ++i)
\r
172 int correctOffset = this->offset; // Assume progressive.
\r
173 if (formatDescription.mode == Interlaced)
\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
182 int dstIndex = i * formatDescription.width * this->pImage->bpp;
\r
183 int srcIndex = (i * this->pImage->width + correctOffset) * this->pImage->bpp;
\r
185 int stopOffset = min(correctOffset + formatDescription .width, this->pImage->width);
\r
186 int size = (stopOffset - correctOffset) * this->pImage->bpp;
\r
188 memcpy(&pFrameData[dstIndex], &pImageData[srcIndex], size);
\r
191 if (formatDescription.mode == Interlaced && !isFirstFrame && !isLastFrame)
\r
192 this->offset += deltaX * 2;
\r
194 this->offset += deltaX;
\r
200 void TargaScrollMediaProducer::Run(HANDLE stopEvent)
\r
202 LOG << LogLevel::Verbose << TEXT("Targa scroll thread started");
\r
204 const short waitHandleCount = 3;
\r
205 HANDLE waitHandles[waitHandleCount] = { stopEvent, initializeEvent_, this->frameBuffer.GetWriteWaitHandle() };
\r
207 int formatWidth = 0;
\r
208 int formatHeight = 0;
\r
210 const FrameFormatDescription& formatDescription = pFrameManager_->GetFrameFormatDescription();
\r
211 formatWidth = formatDescription.width;
\r
212 formatHeight = formatDescription.height;
\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
218 direction = (speed < 0) ? DirectionFlag::ScrollDown : DirectionFlag::ScrollUp;
\r
220 this->speed = abs(speed / formatDescription.fps);
\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
229 if (formatWidth > this->pImage->width || formatHeight > this->pImage->height)
\r
230 PadImageToFrameFormat();
\r
232 bool quitLoop = false;
\r
235 HRESULT waitResult = WaitForMultipleObjects(waitHandleCount, waitHandles, FALSE, 1000);
\r
238 case WAIT_OBJECT_0 + 0: // Stop.
\r
239 case WAIT_FAILED: // Wait failiure.
\r
242 case WAIT_TIMEOUT: // Nothing has happened.
\r
244 case WAIT_OBJECT_0 + 1: //initialize
\r
247 pFrameManager_ = pTempFrameManager_;
\r
248 pTempFrameManager_.reset();
\r
252 case WAIT_OBJECT_0 + 2: // Framebuffer is ready to be filled.
\r
254 // Render next frame.
\r
255 FramePtr pFrame = pFrameManager_->CreateFrame();
\r
256 pFrame = FillVideoFrame(pFrame);
\r
257 this->frameBuffer.push_back(pFrame);
\r
259 // Should we stop scrolling?
\r
260 if ((this->direction == DirectionFlag::ScrollDown || this->direction == DirectionFlag::ScrollRight) && this->offset <= 0)
\r
262 else if (this->direction == DirectionFlag::ScrollUp && this->offset >= (this->pImage->height - formatHeight))
\r
264 else if (this->direction == DirectionFlag::ScrollLeft && this->offset >= (this->pImage->width - formatWidth))
\r
270 // Render a null frame to indicate EOF.
\r
271 FramePtr pNullFrame;
\r
272 this->frameBuffer.push_back(pNullFrame);
\r
274 LOG << LogLevel::Verbose << TEXT("Targa scroll thread ended");
\r
277 bool TargaScrollMediaProducer::OnUnhandledException(const std::exception& ex) throw()
\r
281 FramePtr pNullFrame;
\r
282 this->frameBuffer.push_back(pNullFrame);
\r
284 LOG << LogLevel::Critical << TEXT("UNHANDLED EXCEPTION in targa scroll thread. Message: ") << ex.what();
\r