1 /**
2 * @file
3 * @brief Source file for DecklinkWriter class
4 * @author Jonathan Thomas <jonathan@openshot.org>
5 *
6 * @ref License
7 */
8
9 /* LICENSE
10 *
11 * Copyright (c) 2008-2019 OpenShot Studios, LLC
12 * <http://www.openshotstudios.com/>. This file is part of
13 * OpenShot Library (libopenshot), an open-source project dedicated to
14 * delivering high quality video editing and animation solutions to the
15 * world. For more information visit <http://www.openshot.org/>.
16 *
17 * OpenShot Library (libopenshot) is free software: you can redistribute it
18 * and/or modify it under the terms of the GNU Lesser General Public License
19 * as published by the Free Software Foundation, either version 3 of the
20 * License, or (at your option) any later version.
21 *
22 * OpenShot Library (libopenshot) is distributed in the hope that it will be
23 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU Lesser General Public License for more details.
26 *
27 * You should have received a copy of the GNU Lesser General Public License
28 * along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
29 */
30
31 #include "DecklinkWriter.h"
32
33 using namespace openshot;
34
DecklinkWriter(int device,int video_mode,int pixel_format,int channels,int sample_depth)35 DecklinkWriter::DecklinkWriter(int device, int video_mode, int pixel_format, int channels, int sample_depth)
36 : device(device), is_open(false), g_videoModeIndex(video_mode), g_audioChannels(channels), g_audioSampleDepth(sample_depth)
37 {
38 // Init decklink variables
39 inputFlags = 0;
40 selectedDisplayMode = bmdModeNTSC;
41 pixelFormat = bmdFormat8BitYUV;
42 displayModeCount = 0;
43 exitStatus = 1;
44 foundDisplayMode = false;
45 pthread_mutex_init(&sleepMutex, NULL);
46 pthread_cond_init(&sleepCond, NULL);
47
48 switch(pixel_format)
49 {
50 case 0: pixelFormat = bmdFormat8BitYUV; break;
51 case 1: pixelFormat = bmdFormat10BitYUV; break;
52 case 2: pixelFormat = bmdFormat10BitRGB; break;
53 case 3: pixelFormat = bmdFormat8BitARGB; break;
54 default:
55 throw DecklinkError("Pixel format is not valid (must be 0,1,2,3).");
56 }
57 }
58
59 // Open decklink writer
Open()60 void DecklinkWriter::Open()
61 {
62 // Open reader if not already open
63 if (!is_open)
64 {
65 // Attempt to open blackmagic card
66 deckLinkIterator = CreateDeckLinkIteratorInstance();
67
68 if (!deckLinkIterator)
69 throw DecklinkError("This application requires the DeckLink drivers installed.");
70
71 /* Connect to a DeckLink instance */
72 for (int device_count = 0; device_count <= device; device_count++)
73 {
74 // Check for requested device
75 result = deckLinkIterator->Next(&deckLink);
76 if (result != S_OK)
77 throw DecklinkError("No DeckLink PCI cards found.");
78
79 if (device_count == device)
80 break;
81 }
82
83 if (deckLink->QueryInterface(IID_IDeckLinkOutput, (void**)&deckLinkOutput) != S_OK)
84 throw DecklinkError("DeckLink QueryInterface Failed.");
85
86 // Obtain an IDeckLinkDisplayModeIterator to enumerate the display modes supported on output
87 result = deckLinkOutput->GetDisplayModeIterator(&displayModeIterator);
88 if (result != S_OK)
89 throw DecklinkError("Could not obtain the video output display mode iterator.");
90
91 if (g_videoModeIndex < 0)
92 throw DecklinkError("No video mode specified.");
93
94 // Loop through all available display modes, until a match is found (if any)
95 const char *displayModeName;
96 BMDTimeValue frameRateDuration, frameRateScale;
97
98 while (displayModeIterator->Next(&displayMode) == S_OK)
99 {
100 if (g_videoModeIndex == displayModeCount)
101 {
102 BMDDisplayModeSupport result;
103
104 foundDisplayMode = true;
105 displayMode->GetName(&displayModeName);
106 selectedDisplayMode = displayMode->GetDisplayMode();
107 //deckLinkOutput->DoesSupportVideoMode(selectedDisplayMode, pixelFormat, bmdVideoOutputFlagDefault, &result, NULL);
108
109 // Get framerate
110 displayMode->GetFrameRate(&frameRateDuration, &frameRateScale);
111
112 //if (result == bmdDisplayModeNotSupported)
113 //{
114 // cout << "The display mode does not support the selected pixel format." << endl;
115 // throw DecklinkError("The display mode does not support the selected pixel format.");
116 //}
117
118 break;
119 }
120 displayModeCount++;
121 }
122
123 if (!foundDisplayMode)
124 throw DecklinkError("Invalid video mode. No matching ones found.");
125
126 // Calculate FPS
127 unsigned long m_framesPerSecond = (unsigned long)((frameRateScale + (frameRateDuration-1)) / frameRateDuration);
128
129 // Create Delegate & Pass in pointers to the output and converters
130 delegate = new DeckLinkOutputDelegate(displayMode, deckLinkOutput);
131
132 // Provide this class as a delegate to the audio and video output interfaces
133 deckLinkOutput->SetScheduledFrameCompletionCallback(delegate);
134 //deckLinkOutput->SetAudioCallback(delegate);
135
136 // Check for video input
137 if (deckLinkOutput->EnableVideoOutput(displayMode->GetDisplayMode(), bmdVideoOutputFlagDefault) != S_OK)
138 throw DecklinkError("Failed to enable video output. Is another application using the card?");
139
140 // Check for audio input
141 //if (deckLinkOutput->EnableAudioOutput(bmdAudioSampleRate48kHz, g_audioSampleDepth, g_audioChannels, bmdAudioOutputStreamContinuous) != S_OK)
142 // throw DecklinkError("Failed to enable audio output. Is another application using the card?");
143
144 // Begin video preroll by scheduling a second of frames in hardware
145 //auto f = std::make_shared<Frame>(1, displayMode->GetWidth(), displayMode->GetHeight(), "Blue");
146 //f->AddColor(displayMode->GetWidth(), displayMode->GetHeight(), "Blue");
147
148 // Preroll 1 second of video
149 //for (unsigned i = 0; i < 16; i++)
150 //{
151 // // Write 30 blank frames (for preroll)
152 // delegate->WriteFrame(f);
153 // delegate->ScheduleNextFrame(true);
154 //}
155
156 //deckLinkOutput->StartScheduledPlayback(0, 100, 1.0);
157 //if (deckLinkOutput->BeginAudioPreroll() != S_OK)
158 // throw DecklinkError("Failed to begin audio preroll.");
159
160
161 // Update image properties
162 info.has_audio = true;
163 info.has_video = true;
164 info.vcodec = displayModeName;
165 info.width = displayMode->GetWidth();
166 info.height = displayMode->GetHeight();
167 info.file_size = info.width * info.height * sizeof(char) * 4;
168 info.pixel_ratio.num = 1;
169 info.pixel_ratio.den = 1;
170 info.duration = 60 * 60 * 24; // 24 hour duration... since we're capturing a live stream
171 info.fps.num = frameRateScale;
172 info.fps.den = frameRateDuration;
173 info.video_timebase.num = frameRateDuration;
174 info.video_timebase.den = frameRateScale;
175 info.video_length = round(info.duration * info.fps.ToDouble());
176
177 // Calculate the DAR (display aspect ratio)
178 Fraction size(info.width * info.pixel_ratio.num, info.height * info.pixel_ratio.den);
179
180 // Reduce size fraction
181 size.Reduce();
182
183 // Set the ratio based on the reduced fraction
184 info.display_ratio.num = size.num;
185 info.display_ratio.den = size.den;
186
187 // Mark as "open"
188 is_open = true;
189 }
190 }
191
192 // Close device and video stream
Close()193 void DecklinkWriter::Close()
194 {
195 // Close all objects, if reader is 'open'
196 if (is_open)
197 {
198 // Stop the audio and video output streams immediately
199 deckLinkOutput->StopScheduledPlayback(0, NULL, 0);
200 deckLinkOutput->DisableAudioOutput();
201 deckLinkOutput->DisableVideoOutput();
202
203 // Release DisplayMode
204 displayMode->Release();
205
206 if (displayModeIterator != NULL)
207 {
208 displayModeIterator->Release();
209 displayModeIterator = NULL;
210 }
211
212 if (deckLinkOutput != NULL)
213 {
214 deckLinkOutput->Release();
215 deckLinkOutput = NULL;
216 }
217
218 if (deckLink != NULL)
219 {
220 deckLink->Release();
221 deckLink = NULL;
222 }
223
224 if (deckLinkIterator != NULL)
225 deckLinkIterator->Release();
226
227 // Mark as "closed"
228 is_open = false;
229 }
230 }
231
232 // This method is required for all derived classes of WriterBase. Write a Frame to the video file.
WriteFrame(std::shared_ptr<Frame> frame)233 void DecklinkWriter::WriteFrame(std::shared_ptr<Frame> frame)
234 {
235 // Check for open reader (or throw exception)
236 if (!is_open)
237 throw WriterClosed("The DecklinkWriter is closed. Call Open() before calling this method.");
238
239 delegate->WriteFrame(frame);
240 }
241
242 // This method is required for all derived classes of WriterBase. Write a block of frames from a reader.
WriteFrame(ReaderBase * reader,int start,int length)243 void DecklinkWriter::WriteFrame(ReaderBase* reader, int start, int length)
244 {
245 // Loop through each frame (and encoded it)
246 for (int number = start; number <= length; number++)
247 {
248 // Get the frame
249 std::shared_ptr<Frame> f = reader->GetFrame(number);
250
251 // Encode frame
252 WriteFrame(f);
253 }
254 }
255