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