OpenShot Library | libopenshot  0.1.9
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  * @section LICENSE
7  *
8  * Copyright (c) 2009 Blackmagic Design
9  *
10  * Permission is hereby granted, free of charge, to any person or organization
11  * obtaining a copy of the software and accompanying documentation covered by
12  * this license (the "Software") to use, reproduce, display, distribute,
13  * execute, and transmit the Software, and to prepare derivative works of the
14  * Software, and to permit third-parties to whom the Software is furnished to
15  * do so, all subject to the following:
16  *
17  * The copyright notices in the Software and this entire statement, including
18  * the above license grant, this restriction and the following disclaimer,
19  * must be included in all copies of the Software, in whole or in part, and
20  * all derivative works of the Software, unless such copies or derivative
21  * works are solely in the form of machine-executable object code generated by
22  * a source language processor.
23  *
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26  * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
27  * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
28  * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
29  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30  * DEALINGS IN THE SOFTWARE.
31  *
32  *
33  * Copyright (c) 2008-2014 OpenShot Studios, LLC
34  * <http://www.openshotstudios.com/>. This file is part of
35  * OpenShot Library (libopenshot), an open-source project dedicated to
36  * delivering high quality video editing and animation solutions to the
37  * world. For more information visit <http://www.openshot.org/>.
38  *
39  * OpenShot Library (libopenshot) is free software: you can redistribute it
40  * and/or modify it under the terms of the GNU Lesser General Public License
41  * as published by the Free Software Foundation, either version 3 of the
42  * License, or (at your option) any later version.
43  *
44  * OpenShot Library (libopenshot) is distributed in the hope that it will be
45  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
47  * GNU Lesser General Public License for more details.
48  *
49  * You should have received a copy of the GNU Lesser General Public License
50  * along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
51  */
52 
53 #include "../include/DecklinkOutput.h"
54 
55 using namespace std;
56 
57 DeckLinkOutputDelegate::DeckLinkOutputDelegate(IDeckLinkDisplayMode *displayMode, IDeckLinkOutput* m_deckLinkOutput)
58  : m_refCount(0), displayMode(displayMode), width(0), height(0)
59 {
60  // reference to output device
61  deckLinkOutput = m_deckLinkOutput;
62 
63  // init some variables
66  m_audioSampleRate = bmdAudioSampleRate48kHz;
67  m_audioSampleDepth = 16;
69  m_currentFrame = NULL;
70 
71  // Get framerate
72  displayMode->GetFrameRate(&frameRateDuration, &frameRateScale);
74 
75  // Allocate audio array
78 
79  // Zero the buffer (interpreted as audio silence)
81  audioSamplesPerFrame = (unsigned long)((m_audioSampleRate * frameRateDuration) / frameRateScale);
82 
83  pthread_mutex_init(&m_mutex, NULL);
84 }
85 
87 {
88  cout << "DESTRUCTOR!!!" << endl;
89  pthread_mutex_destroy(&m_mutex);
90 }
91 
92 /************************* DeckLink API Delegate Methods *****************************/
93 HRESULT DeckLinkOutputDelegate::ScheduledFrameCompleted (IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult result)
94 {
95  //cout << "Scheduled Successfully!" << endl;
96 
97  // When a video frame has been released by the API, schedule another video frame to be output
98  ScheduleNextFrame(false);
99 
100  return S_OK;
101 }
102 
104 {
105  //cout << "PLAYBACK HAS STOPPED!!!" << endl;
106  return S_OK;
107 }
108 
110 {
111 // // Provide further audio samples to the DeckLink API until our preferred buffer waterlevel is reached
112 // const unsigned long kAudioWaterlevel = 48000;
113 // unsigned int bufferedSamples;
114 //
115 // // Try to maintain the number of audio samples buffered in the API at a specified waterlevel
116 // if ((deckLinkOutput->GetBufferedAudioSampleFrameCount(&bufferedSamples) == S_OK) && (bufferedSamples < kAudioWaterlevel))
117 // {
118 // unsigned int samplesToEndOfBuffer;
119 // unsigned int samplesToWrite;
120 // unsigned int samplesWritten;
121 //
122 // samplesToEndOfBuffer = (m_audioBufferSampleLength - m_audioBufferOffset);
123 // samplesToWrite = (kAudioWaterlevel - bufferedSamples);
124 // if (samplesToWrite > samplesToEndOfBuffer)
125 // samplesToWrite = samplesToEndOfBuffer;
126 //
127 // if (deckLinkOutput->ScheduleAudioSamples((void*)((unsigned long)m_audioBuffer + (m_audioBufferOffset * m_audioChannelCount * m_audioSampleDepth/8)), samplesToWrite, 0, 0, &samplesWritten) == S_OK)
128 // {
129 // m_audioBufferOffset = ((m_audioBufferOffset + samplesWritten) % m_audioBufferSampleLength);
130 // }
131 // }
132 //
133 //
134 // if (preroll)
135 // {
136 // // Start audio and video output
137 // deckLinkOutput->StartScheduledPlayback(0, 100, 1.0);
138 // }
139 
140  return S_OK;
141 }
142 
143 // Schedule the next frame
145 {
146  // Get oldest frame (if any)
147  if (final_frames.size() > 0)
148  {
149  #pragma omp critical (blackmagic_output_queue)
150  {
151  // Get the next frame off the queue
152  uint8_t* castBytes = final_frames.front();
153  final_frames.pop_front(); // remove this frame from the queue
154 
155  // Release the current frame (if any)
156  if (m_currentFrame)
157  {
158  m_currentFrame->Release();
159  m_currentFrame = NULL;
160  }
161 
162  // Create a new one
163  while (deckLinkOutput->CreateVideoFrame(
164  width,
165  height,
166  width * 4,
167  bmdFormat8BitARGB,
168  bmdFrameFlagDefault,
169  &m_currentFrame) != S_OK)
170  {
171  cout << "failed to create video frame" << endl;
172  usleep(1000 * 1);
173  }
174 
175  // Copy pixel data to frame
176  void *frameBytesDest;
177  m_currentFrame->GetBytes(&frameBytesDest);
178  memcpy(frameBytesDest, castBytes, width * height * 4);
179 
180  // Delete temp array
181  delete[] castBytes;
182  castBytes = NULL;
183 
184  } // critical
185  }
186  //else
187  // cout << "Queue: empty on writer..." << endl;
188 
189  // Schedule a frame to be displayed
190  if (m_currentFrame && deckLinkOutput->ScheduleVideoFrame(m_currentFrame, (m_totalFramesScheduled * frameRateDuration), frameRateDuration, frameRateScale) != S_OK)
191  cout << "ScheduleVideoFrame FAILED!!! " << m_totalFramesScheduled << endl;
192 
193  // Update the timestamp (regardless of previous frame's success)
195 
196 }
197 
198 void DeckLinkOutputDelegate::WriteFrame(std::shared_ptr<openshot::Frame> frame)
199 {
200 
201  #pragma omp critical (blackmagic_output_queue)
202  // Add raw OpenShot frame object
203  raw_video_frames.push_back(frame);
204 
205 
206  // Process frames once we have a few (to take advantage of multiple threads)
208  {
209 
210  //omp_set_num_threads(1);
211  omp_set_nested(true);
212  #pragma omp parallel
213  {
214  #pragma omp single
215  {
216  // Temp frame counters (to keep the frames in order)
217  frameCount = 0;
218 
219  // Loop through each queued image frame
220  while (!raw_video_frames.empty())
221  {
222  // Get front frame (from the queue)
223  std::shared_ptr<openshot::Frame> frame = raw_video_frames.front();
224  raw_video_frames.pop_front();
225 
226  // copy of frame count
227  unsigned long copy_frameCount(frameCount);
228 
229  #pragma omp task firstprivate(frame, copy_frameCount)
230  {
231  // *********** CONVERT YUV source frame to RGB ************
232  void *frameBytes;
233  void *audioFrameBytes;
234 
235  width = frame->GetWidth();
236  height = frame->GetHeight();
237 
238  // Get RGB Byte array
239  int numBytes = frame->GetHeight() * frame->GetWidth() * 4;
240  uint8_t *castBytes = new uint8_t[numBytes];
241 
242  // TODO: Fix Decklink support with QImage Upgrade
243  // Get a list of pixels in our frame's image. Each pixel is represented by
244  // a PixelPacket struct, which has 4 properties: .red, .blue, .green, .alpha
245 // const Magick::PixelPacket *pixel_packets = frame->GetPixels();
246 //
247 // // loop through ImageMagic pixel structs, and put the colors in a regular array, and move the
248 // // colors around to match the Decklink order (ARGB).
249 // for (int packet = 0, row = 0; row < numBytes; packet++, row+=4)
250 // {
251 // // Update buffer (which is already linked to the AVFrame: pFrameRGB)
252 // // Each color needs to be scaled to 8 bit (using the ImageMagick built-in ScaleQuantumToChar function)
253 // castBytes[row] = MagickCore::ScaleQuantumToChar((Magick::Quantum) 0); // alpha
254 // castBytes[row+1] = MagickCore::ScaleQuantumToChar((Magick::Quantum) pixel_packets[packet].red);
255 // castBytes[row+2] = MagickCore::ScaleQuantumToChar((Magick::Quantum) pixel_packets[packet].green);
256 // castBytes[row+3] = MagickCore::ScaleQuantumToChar((Magick::Quantum) pixel_packets[packet].blue);
257 // }
258 
259  #pragma omp critical (blackmagic_output_queue)
260  {
261  //if (20 == frame->number)
262  // frame->Display();
263  // Add processed frame to cache (to be recalled in order after the thread pool is done)
264  temp_cache[copy_frameCount] = castBytes;
265  }
266 
267  } // end task
268 
269  // Increment frame count
270  frameCount++;
271 
272  } // end while
273  } // omp single
274  } // omp parallel
275 
276 
277  // Add frames to final queue (in order)
278  #pragma omp critical (blackmagic_output_queue)
279  for (int z = 0; z < frameCount; z++)
280  {
281  // Add to final queue
282  final_frames.push_back(temp_cache[z]);
283  }
284 
285  // Clear temp cache
286  temp_cache.clear();
287 
288 
289  //cout << "final_frames.size(): " << final_frames.size() << ", raw_video_frames.size(): " << raw_video_frames.size() << endl;
291  {
292  cout << "Prerolling!" << endl;
293 
294  for (int x = 0; x < final_frames.size(); x++)
295  ScheduleNextFrame(true);
296 
297  cout << "Starting scheduled playback!" << endl;
298 
299  // Start playback when enough frames have been processed
300  deckLinkOutput->StartScheduledPlayback(0, 100, 1.0);
301  }
302  else
303  {
304  // Be sure we don't have too many extra frames
305  #pragma omp critical (blackmagic_output_queue)
306  while (final_frames.size() > (m_framesPerSecond + 15))
307  {
308  //cout << "Too many, so remove some..." << endl;
309  // Remove oldest frame
310  delete[] final_frames.front();
311  final_frames.pop_front();
312  }
313  }
314 
315 
316  } // if
317 
318 }
virtual HRESULT STDMETHODCALLTYPE ScheduledFrameCompleted(IDeckLinkVideoFrame *completedFrame, BMDOutputFrameCompletionResult result)
DeckLinkOutputDelegate(IDeckLinkDisplayMode *displayMode, IDeckLinkOutput *deckLinkOutput)
#define OPEN_MP_NUM_PROCESSORS
BMDTimeValue frameRateScale
unsigned long m_audioSampleDepth
IDeckLinkMutableVideoFrame * m_currentFrame
unsigned long frameCount
deque< uint8_t *> final_frames
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
deque< std::shared_ptr< openshot::Frame > > raw_video_frames
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