1 /**
2  * @file
3  * @brief Source file for DecklinkInput class
4  * @author Jonathan Thomas <jonathan@openshot.org>, Blackmagic Design
5  *
6  * @ref License
7  */
8 
9 /* LICENSE
10  *
11  * Copyright (c) 2009 Blackmagic Design
12  *
13  * Permission is hereby granted, free of charge, to any person or organization
14  * obtaining a copy of the software and accompanying documentation covered by
15  * this license (the "Software") to use, reproduce, display, distribute,
16  * execute, and transmit the Software, and to prepare derivative works of the
17  * Software, and to permit third-parties to whom the Software is furnished to
18  * do so, all subject to the following:
19  *
20  * The copyright notices in the Software and this entire statement, including
21  * the above license grant, this restriction and the following disclaimer,
22  * must be included in all copies of the Software, in whole or in part, and
23  * all derivative works of the Software, unless such copies or derivative
24  * works are solely in the form of machine-executable object code generated by
25  * a source language processor.
26  *
27  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
30  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
31  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
32  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
33  * DEALINGS IN THE SOFTWARE.
34  *
35  *
36  * Copyright (c) 2008-2019 OpenShot Studios, LLC
37  * <http://www.openshotstudios.com/>. This file is part of
38  * OpenShot Library (libopenshot), an open-source project dedicated to
39  * delivering high quality video editing and animation solutions to the
40  * world. For more information visit <http://www.openshot.org/>.
41  *
42  * OpenShot Library (libopenshot) is free software: you can redistribute it
43  * and/or modify it under the terms of the GNU Lesser General Public License
44  * as published by the Free Software Foundation, either version 3 of the
45  * License, or (at your option) any later version.
46  *
47  * OpenShot Library (libopenshot) is distributed in the hope that it will be
48  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
49  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
50  * GNU Lesser General Public License for more details.
51  *
52  * You should have received a copy of the GNU Lesser General Public License
53  * along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
54  */
55 
56 #include "DecklinkInput.h"
57 
58 using namespace std;
59 
DeckLinkInputDelegate(pthread_cond_t * m_sleepCond,IDeckLinkOutput * m_deckLinkOutput,IDeckLinkVideoConversion * m_deckLinkConverter)60 DeckLinkInputDelegate::DeckLinkInputDelegate(pthread_cond_t* m_sleepCond, IDeckLinkOutput* m_deckLinkOutput, IDeckLinkVideoConversion* m_deckLinkConverter)
61  : m_refCount(0), g_timecodeFormat(0), frameCount(0), final_frameCount(0)
62 {
63 	sleepCond = m_sleepCond;
64 	deckLinkOutput = m_deckLinkOutput;
65 	deckLinkConverter = m_deckLinkConverter;
66 
67 	// Set cache size (20 1080p frames)
68 	final_frames.SetMaxBytes(60 * 1920 * 1080 * 4 + (44100 * 2 * 4));
69 
70 	pthread_mutex_init(&m_mutex, NULL);
71 }
72 
~DeckLinkInputDelegate()73 DeckLinkInputDelegate::~DeckLinkInputDelegate()
74 {
75 	pthread_mutex_destroy(&m_mutex);
76 }
77 
AddRef(void)78 ULONG DeckLinkInputDelegate::AddRef(void)
79 {
80 	pthread_mutex_lock(&m_mutex);
81 		m_refCount++;
82 	pthread_mutex_unlock(&m_mutex);
83 
84 	return (ULONG)m_refCount;
85 }
86 
Release(void)87 ULONG DeckLinkInputDelegate::Release(void)
88 {
89 	pthread_mutex_lock(&m_mutex);
90 		m_refCount--;
91 	pthread_mutex_unlock(&m_mutex);
92 
93 	if (m_refCount == 0)
94 	{
95 		delete this;
96 		return 0;
97 	}
98 
99 	return (ULONG)m_refCount;
100 }
101 
GetCurrentFrameNumber()102 unsigned long DeckLinkInputDelegate::GetCurrentFrameNumber()
103 {
104 	if (final_frameCount > 0)
105 		return final_frameCount - 1;
106 	else
107 		return 0;
108 }
109 
GetFrame(int64_t requested_frame)110 std::shared_ptr<openshot::Frame> DeckLinkInputDelegate::GetFrame(int64_t requested_frame)
111 {
112 	std::shared_ptr<openshot::Frame> f;
113 
114 	// Is this frame for the future?
115 	while (requested_frame > GetCurrentFrameNumber())
116 	{
117 		usleep(500 * 1);
118 	}
119 
120 	#pragma omp critical (blackmagic_input_queue)
121 	{
122 		if (final_frames.Exists(requested_frame))
123 		{
124 			// Get the frame and remove it from the cache
125 			f = final_frames.GetFrame(requested_frame);
126 			final_frames.Remove(requested_frame);
127 		}
128 		else
129 		{
130 			cout << "Can't find " << requested_frame << ", GetCurrentFrameNumber(): " << GetCurrentFrameNumber() << endl;
131 			final_frames.Display();
132 		}
133 	}
134 
135 	return f;
136 }
137 
VideoInputFrameArrived(IDeckLinkVideoInputFrame * videoFrame,IDeckLinkAudioInputPacket * audioFrame)138 HRESULT DeckLinkInputDelegate::VideoInputFrameArrived(IDeckLinkVideoInputFrame* videoFrame, IDeckLinkAudioInputPacket* audioFrame)
139 {
140 	// Handle Video Frame
141 	if(videoFrame)
142 	{
143 
144 		if (videoFrame->GetFlags() & bmdFrameHasNoInputSource)
145 		{
146 			fprintf(stderr, "Frame received (#%lu) - No input signal detected\n", frameCount);
147 		}
148 		else
149 		{
150 			const char *timecodeString = NULL;
151 			if (g_timecodeFormat != 0)
152 			{
153 				IDeckLinkTimecode *timecode;
154 				if (videoFrame->GetTimecode(g_timecodeFormat, &timecode) == S_OK)
155 				{
156 					timecode->GetString(&timecodeString);
157 				}
158 			}
159 
160 //			fprintf(stderr, "Frame received (#%lu) [%s] - Size: %li bytes\n",
161 //				frameCount,
162 //				timecodeString != NULL ? timecodeString : "No timecode",
163 //				videoFrame->GetRowBytes() * videoFrame->GetHeight());
164 
165 			if (timecodeString)
166 				free((void*)timecodeString);
167 
168 			// Create a new copy of the YUV frame object
169 			IDeckLinkMutableVideoFrame *m_yuvFrame = NULL;
170 
171 			int width = videoFrame->GetWidth();
172 			int height = videoFrame->GetHeight();
173 
174 			HRESULT res = deckLinkOutput->CreateVideoFrame(
175 									width,
176 									height,
177 									videoFrame->GetRowBytes(),
178 									bmdFormat8BitYUV,
179 									bmdFrameFlagDefault,
180 									&m_yuvFrame);
181 
182 			// Copy pixel and audio to copied frame
183 			void *frameBytesSource;
184 			void *frameBytesDest;
185 			videoFrame->GetBytes(&frameBytesSource);
186 			m_yuvFrame->GetBytes(&frameBytesDest);
187 			memcpy(frameBytesDest, frameBytesSource, videoFrame->GetRowBytes() * height);
188 
189 			// Add raw YUV frame to queue
190 			raw_video_frames.push_back(m_yuvFrame);
191 
192 			// Process frames once we have a few (to take advantage of multiple threads)
193 			int number_to_process = raw_video_frames.size();
194 			if (number_to_process >= OPEN_MP_NUM_PROCESSORS)
195 			{
196 
197 //omp_set_num_threads(1);
198 omp_set_nested(true);
199 #pragma omp parallel
200 {
201 #pragma omp single
202 {
203 				// Temp frame counters (to keep the frames in order)
204 				//frameCount = 0;
205 
206 				// Loop through each queued image frame
207 				while (!raw_video_frames.empty())
208 				{
209 					// Get front frame (from the queue)
210 					IDeckLinkMutableVideoFrame* frame = raw_video_frames.front();
211 					raw_video_frames.pop_front();
212 
213 					// declare local variables (for OpenMP)
214 					IDeckLinkOutput *copy_deckLinkOutput(deckLinkOutput);
215 					IDeckLinkVideoConversion *copy_deckLinkConverter(deckLinkConverter);
216 					unsigned long copy_frameCount(frameCount);
217 
218 					#pragma omp task firstprivate(copy_deckLinkOutput, copy_deckLinkConverter, frame, copy_frameCount)
219 					{
220 						// *********** CONVERT YUV source frame to RGB ************
221 						void *frameBytes;
222 						void *audioFrameBytes;
223 
224 						// Create a new RGB frame object
225 						IDeckLinkMutableVideoFrame *m_rgbFrame = NULL;
226 
227 						int width = videoFrame->GetWidth();
228 						int height = videoFrame->GetHeight();
229 
230 						HRESULT res = copy_deckLinkOutput->CreateVideoFrame(
231 												width,
232 												height,
233 												width * 4,
234 												bmdFormat8BitARGB,
235 												bmdFrameFlagDefault,
236 												&m_rgbFrame);
237 
238 						if(res != S_OK)
239 							cout << "BMDOutputDelegate::StartRunning: Error creating RGB frame, res:" << res << endl;
240 
241 						// Create a RGB version of this YUV video frame
242 						copy_deckLinkConverter->ConvertFrame(frame, m_rgbFrame);
243 
244 						// Get RGB Byte array
245 						m_rgbFrame->GetBytes(&frameBytes);
246 
247 						// *********** CREATE OPENSHOT FRAME **********
248 						auto f = std::make_shared<openshot::Frame>(
249                             copy_frameCount, width, height, "#000000", 2048, 2);
250 
251 						// Add Image data to openshot frame
252 						// TODO: Fix Decklink support with QImage Upgrade
253 						//f->AddImage(width, height, "ARGB", Magick::CharPixel, (uint8_t*)frameBytes);
254 
255 						#pragma omp critical (blackmagic_input_queue)
256 						{
257 							// Add processed frame to cache (to be recalled in order after the thread pool is done)
258 							final_frames.Add(f);
259 						}
260 
261 						// Release RGB data
262 						if (m_rgbFrame)
263 							m_rgbFrame->Release();
264 						// Release RGB data
265 						if (frame)
266 							frame->Release();
267 
268 					} // end task
269 
270 					// Increment frame count
271 					frameCount++;
272 
273 				} // end while
274 
275 } // omp single
276 } // omp parallel
277 
278 				// Update final frameCount (since they are done processing now)
279 				final_frameCount += number_to_process;
280 
281 
282 			} // if size > num processors
283 		} // has video source
284 	} // if videoFrame
285 
286     return S_OK;
287 }
288 
VideoInputFormatChanged(BMDVideoInputFormatChangedEvents events,IDeckLinkDisplayMode * mode,BMDDetectedVideoInputFormatFlags)289 HRESULT DeckLinkInputDelegate::VideoInputFormatChanged(BMDVideoInputFormatChangedEvents events, IDeckLinkDisplayMode *mode, BMDDetectedVideoInputFormatFlags)
290 {
291     return S_OK;
292 }
293