OpenShot Library | libopenshot  0.2.5
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 "../include/DecklinkOutput.h"
57 
58 using namespace std;
59 
60 DeckLinkOutputDelegate::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;
70  m_audioSampleDepth = 16;
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 *****************************/
96 HRESULT 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 
201 void 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 }
@ 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