OpenShot Library | libopenshot 0.2.7
DecklinkOutput.cpp
Go to the documentation of this file.
1/**
2 * @file
3 * @brief Source file for DecklinkOutput 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 "DecklinkOutput.h"
57
58using namespace std;
59
60DeckLinkOutputDelegate::DeckLinkOutputDelegate(IDeckLinkDisplayMode *displayMode, IDeckLinkOutput* m_deckLinkOutput)
61 : m_refCount(0), displayMode(displayMode), width(0), height(0)
62{
63 // reference to output device
64 deckLinkOutput = m_deckLinkOutput;
65
66 // init some variables
69 m_audioSampleRate = bmdAudioSampleRate48kHz;
72 m_currentFrame = NULL;
73
74 // Get framerate
77
78 // Allocate audio array
81
82 // Zero the buffer (interpreted as audio silence)
85
86 pthread_mutex_init(&m_mutex, NULL);
87}
88
90{
91 cout << "DESTRUCTOR!!!" << endl;
92 pthread_mutex_destroy(&m_mutex);
93}
94
95/************************* DeckLink API Delegate Methods *****************************/
96HRESULT DeckLinkOutputDelegate::ScheduledFrameCompleted (IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult result)
97{
98 //cout << "Scheduled Successfully!" << endl;
99
100 // When a video frame has been released by the API, schedule another video frame to be output
101 ScheduleNextFrame(false);
102
103 return S_OK;
104}
105
107{
108 //cout << "PLAYBACK HAS STOPPED!!!" << endl;
109 return S_OK;
110}
111
113{
114// // Provide further audio samples to the DeckLink API until our preferred buffer waterlevel is reached
115// const unsigned long kAudioWaterlevel = 48000;
116// unsigned int bufferedSamples;
117//
118// // Try to maintain the number of audio samples buffered in the API at a specified waterlevel
119// if ((deckLinkOutput->GetBufferedAudioSampleFrameCount(&bufferedSamples) == S_OK) && (bufferedSamples < kAudioWaterlevel))
120// {
121// unsigned int samplesToEndOfBuffer;
122// unsigned int samplesToWrite;
123// unsigned int samplesWritten;
124//
125// samplesToEndOfBuffer = (m_audioBufferSampleLength - m_audioBufferOffset);
126// samplesToWrite = (kAudioWaterlevel - bufferedSamples);
127// if (samplesToWrite > samplesToEndOfBuffer)
128// samplesToWrite = samplesToEndOfBuffer;
129//
130// if (deckLinkOutput->ScheduleAudioSamples((void*)((unsigned long)m_audioBuffer + (m_audioBufferOffset * m_audioChannelCount * m_audioSampleDepth/8)), samplesToWrite, 0, 0, &samplesWritten) == S_OK)
131// {
132// m_audioBufferOffset = ((m_audioBufferOffset + samplesWritten) % m_audioBufferSampleLength);
133// }
134// }
135//
136//
137// if (preroll)
138// {
139// // Start audio and video output
140// deckLinkOutput->StartScheduledPlayback(0, 100, 1.0);
141// }
142
143 return S_OK;
144}
145
146// Schedule the next frame
148{
149 // Get oldest frame (if any)
150 if (final_frames.size() > 0)
151 {
152 #pragma omp critical (blackmagic_output_queue)
153 {
154 // Get the next frame off the queue
155 uint8_t* castBytes = final_frames.front();
156 final_frames.pop_front(); // remove this frame from the queue
157
158 // Release the current frame (if any)
159 if (m_currentFrame)
160 {
161 m_currentFrame->Release();
162 m_currentFrame = NULL;
163 }
164
165 // Create a new one
166 while (deckLinkOutput->CreateVideoFrame(
167 width,
168 height,
169 width * 4,
170 bmdFormat8BitARGB,
171 bmdFrameFlagDefault,
172 &m_currentFrame) != S_OK)
173 {
174 cout << "failed to create video frame" << endl;
175 usleep(1000 * 1);
176 }
177
178 // Copy pixel data to frame
179 void *frameBytesDest;
180 m_currentFrame->GetBytes(&frameBytesDest);
181 memcpy(frameBytesDest, castBytes, width * height * 4);
182
183 // Delete temp array
184 delete[] castBytes;
185 castBytes = NULL;
186
187 } // critical
188 }
189 //else
190 // cout << "Queue: empty on writer..." << endl;
191
192 // Schedule a frame to be displayed
194 cout << "ScheduleVideoFrame FAILED!!! " << m_totalFramesScheduled << endl;
195
196 // Update the timestamp (regardless of previous frame's success)
198
199}
200
201void DeckLinkOutputDelegate::WriteFrame(std::shared_ptr<openshot::Frame> frame)
202{
203
204 #pragma omp critical (blackmagic_output_queue)
205 // Add raw OpenShot frame object
206 raw_video_frames.push_back(frame);
207
208
209 // Process frames once we have a few (to take advantage of multiple threads)
211 {
212
213 //omp_set_num_threads(1);
214 omp_set_nested(true);
215 #pragma omp parallel
216 {
217 #pragma omp single
218 {
219 // Temp frame counters (to keep the frames in order)
220 frameCount = 0;
221
222 // Loop through each queued image frame
223 while (!raw_video_frames.empty())
224 {
225 // Get front frame (from the queue)
226 std::shared_ptr<openshot::Frame> frame = raw_video_frames.front();
227 raw_video_frames.pop_front();
228
229 // copy of frame count
230 unsigned long copy_frameCount(frameCount);
231
232 #pragma omp task firstprivate(frame, copy_frameCount)
233 {
234 // *********** CONVERT YUV source frame to RGB ************
235 void *frameBytes;
236 void *audioFrameBytes;
237
238 width = frame->GetWidth();
239 height = frame->GetHeight();
240
241 // Get RGB Byte array
242 int numBytes = frame->GetHeight() * frame->GetWidth() * 4;
243 uint8_t *castBytes = new uint8_t[numBytes];
244
245 // TODO: Fix Decklink support with QImage Upgrade
246 // Get a list of pixels in our frame's image. Each pixel is represented by
247 // a PixelPacket struct, which has 4 properties: .red, .blue, .green, .alpha
248// const Magick::PixelPacket *pixel_packets = frame->GetPixels();
249//
250// // loop through ImageMagic pixel structs, and put the colors in a regular array, and move the
251// // colors around to match the Decklink order (ARGB).
252// for (int packet = 0, row = 0; row < numBytes; packet++, row+=4)
253// {
254// // Update buffer (which is already linked to the AVFrame: pFrameRGB)
255// // Each color needs to be scaled to 8 bit (using the ImageMagick built-in ScaleQuantumToChar function)
256// castBytes[row] = MagickCore::ScaleQuantumToChar((Magick::Quantum) 0); // alpha
257// castBytes[row+1] = MagickCore::ScaleQuantumToChar((Magick::Quantum) pixel_packets[packet].red);
258// castBytes[row+2] = MagickCore::ScaleQuantumToChar((Magick::Quantum) pixel_packets[packet].green);
259// castBytes[row+3] = MagickCore::ScaleQuantumToChar((Magick::Quantum) pixel_packets[packet].blue);
260// }
261
262 #pragma omp critical (blackmagic_output_queue)
263 {
264 //if (20 == frame->number)
265 // frame->Display();
266 // Add processed frame to cache (to be recalled in order after the thread pool is done)
267 temp_cache[copy_frameCount] = castBytes;
268 }
269
270 } // end task
271
272 // Increment frame count
273 frameCount++;
274
275 } // end while
276 } // omp single
277 } // omp parallel
278
279
280 // Add frames to final queue (in order)
281 #pragma omp critical (blackmagic_output_queue)
282 for (int z = 0; z < frameCount; z++)
283 {
284 // Add to final queue
285 final_frames.push_back(temp_cache[z]);
286 }
287
288 // Clear temp cache
289 temp_cache.clear();
290
291
292 //cout << "final_frames.size(): " << final_frames.size() << ", raw_video_frames.size(): " << raw_video_frames.size() << endl;
294 {
295 cout << "Prerolling!" << endl;
296
297 for (int x = 0; x < final_frames.size(); x++)
298 ScheduleNextFrame(true);
299
300 cout << "Starting scheduled playback!" << endl;
301
302 // Start playback when enough frames have been processed
303 deckLinkOutput->StartScheduledPlayback(0, 100, 1.0);
304 }
305 else
306 {
307 // Be sure we don't have too many extra frames
308 #pragma omp critical (blackmagic_output_queue)
309 while (final_frames.size() > (m_framesPerSecond + 15))
310 {
311 //cout << "Too many, so remove some..." << endl;
312 // Remove oldest frame
313 delete[] final_frames.front();
314 final_frames.pop_front();
315 }
316 }
317
318
319 } // if
320
321}
Header file for DecklinkOutput class.
@ kOutputSignalDrop
#define OPEN_MP_NUM_PROCESSORS
unsigned long m_audioBufferSampleLength
void WriteFrame(std::shared_ptr< openshot::Frame > frame)
Custom method to write new frames.
unsigned long m_totalFramesScheduled
virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame *completedFrame, BMDOutputFrameCompletionResult result)
IDeckLinkMutableVideoFrame * m_currentFrame
IDeckLinkDisplayMode * displayMode
virtual HRESULT STDMETHODCALLTYPE RenderAudioSamples(bool preroll)
IDeckLinkOutput * deckLinkOutput
std::deque< uint8_t * > final_frames
unsigned long frameCount
unsigned long m_audioSampleDepth
BMDAudioSampleRate m_audioSampleRate
void ScheduleNextFrame(bool prerolling)
Schedule the next frame.
DeckLinkOutputDelegate(IDeckLinkDisplayMode *displayMode, IDeckLinkOutput *deckLinkOutput)
virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped()
unsigned long audioSamplesPerFrame
BMDTimeValue frameRateScale
std::deque< std::shared_ptr< openshot::Frame > > raw_video_frames
std::map< int, uint8_t * > temp_cache
unsigned long m_audioChannelCount
unsigned long m_framesPerSecond
OutputSignal m_outputSignal
BMDTimeValue frameRateDuration