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