3 * MLT SDI Consumer: request video and audio data from MLT and generate an SDI stream
4 * Copyright (C) 2008 Broadcasting Center Europe S.A. All rights reserved.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 * This software act as interface between the MLT Frameworkas as
24 * MLT Consumer and the Linear Systems Ltd. SMPTE 259M-C boards.
26 * Linear Systems can be contacted at http://www.linsys.ca
28 * ---------------------------------------------------------------
29 * System : INTeL I686 64Bit
30 * OS : Linux SuSE Kernel 2.6.27.23-0.1-default
32 * ---------------------------------------------------------------
33 * Project : MLT SDI Consumer for SD
34 * Started by : Thomas Kurpick, Dipl.Inf. (FH)
35 * ---------------------------------------------------------------
36 * This program writes an SDI stream to the SDI driver provided
37 * device files. E.g. SDI device file:
46 * SDI Master™ FD PCIe LP
47 * SDI Master™ Quad/o PCIe LP
52 * consumer_SDIstream.c
53 * Consumer for MLT Framework > 0.2.4 (release 0.2.4 does NOT work, profiles where not implemented)
56 * inigo video.dv -consumer linsys_sdi:/dev/sditx0 buffer=0;
57 * melt video.dv -consumer linsys_sdi:/dev/sditx0 buffer=0;
61 #include <framework/mlt_frame.h>
62 #include <framework/mlt_consumer.h>
63 #include <framework/mlt_log.h>
71 #include "sdi_generator.c"
73 typedef struct consumer_SDIstream_s *consumer_SDIstream;
75 struct consumer_SDIstream_s {
77 struct mlt_consumer_s parent; // This is the basic Consumer from which we fetch our data
78 char *path_destination_sdi; // Path for SDI output
79 int16_t audio_buffer[MAX_AUDIO_STREAMS][MAX_AUDIO_SAMPLES]; // The SDI audio channel pairs for this frame
82 /** Forward references to static functions.
85 static int consumer_start(mlt_consumer this);
86 static int consumer_stop(mlt_consumer this);
87 static int consumer_is_stopped(mlt_consumer this);
88 static void consumer_close(mlt_consumer parent);
89 static void *consumer_thread(void *);
91 //*****************************************************************************************************
92 //*****************************************************************************************************
93 //*******************************************SDI Master Consumer***************************************
94 //*****************************************************************************************************
95 //*****************************************************************************************************
99 * This is what will be called by the factory
100 * @param profile: profile name for consumer
101 * @param type: unused
103 * @param *arg: pointer to output path
105 mlt_consumer consumer_SDIstream_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) {
107 // Create the consumer object
108 consumer_SDIstream this = calloc(sizeof(struct consumer_SDIstream_s), 1);
110 // If malloc and consumer init ok
111 if (this != NULL && mlt_consumer_init(&this->parent, this, profile) == 0) {
112 // Get the parent consumer object
113 mlt_consumer parent = &this->parent;
115 // We have stuff to clean up, so override the close method
116 parent->close = consumer_close;
118 // Set output path for SDI, default is "/dev/sditx0"
120 this->path_destination_sdi = strdup("/dev/sditx0");
122 this->path_destination_sdi = strdup(arg);
125 // Set up start/stop/terminated callbacks
126 parent->start = consumer_start;
127 parent->stop = consumer_stop;
128 parent->is_stopped = consumer_is_stopped;
130 // Hardcode the audio sample rate to 48KHz for SDI
131 mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(parent), "frequency", 48000);
133 // Return the consumer produced
137 // malloc or consumer init failed
145 * Start the consumer.
147 static int consumer_start(mlt_consumer parent) {
148 // Get the properties
149 mlt_properties properties = mlt_consumer_properties(parent);
151 // Get the actual object
152 consumer_SDIstream this = parent->child;
154 // Check that we're not already running
155 if (!mlt_properties_get_int(properties, "running")) {
157 pthread_t *consumer_pthread = calloc(1, sizeof(pthread_t));
159 // Assign the thread to properties
160 mlt_properties_set_data(properties, "consumer_pthread", consumer_pthread, sizeof(pthread_t), free, NULL);
162 // Set the running state
163 mlt_properties_set_int(properties, "running", 1);
165 // Create the the threads
166 pthread_create(consumer_pthread, NULL, consumer_thread, this);
174 static int consumer_stop(mlt_consumer parent) {
176 // Get the properties
177 mlt_properties properties = mlt_consumer_properties(parent);
179 // Check that we're running
180 if (mlt_properties_get_int(properties, "running")) {
182 pthread_t *consumer_pthread = mlt_properties_get_data(properties, "consumer_pthread", NULL);
185 mlt_properties_set_int(properties, "running", 0);
187 // Wait for termination
188 pthread_join(*consumer_pthread, NULL);
194 * Determine if the consumer is stopped.
196 static int consumer_is_stopped(mlt_consumer this) {
197 // Get the properties
198 mlt_properties properties = mlt_consumer_properties(this);
199 return !mlt_properties_get_int(properties, "running");
203 * Threaded wrapper for pipe.
205 static void *consumer_thread(void *arg) {
207 consumer_SDIstream this = arg;
210 mlt_consumer consumer = &this->parent;
212 // Convenience functionality (this is to stop melt/inigo after the end of a playout)
213 int terminate_on_pause = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "terminate_on_pause");
216 // Define a frame pointer
219 if (!sdimaster_init(this->path_destination_sdi, 0)) {
223 // set Datablock number for SDI encoding
226 double fps = mlt_properties_get_double(MLT_CONSUMER_PROPERTIES(consumer), "fps");
227 unsigned int count = 0;
229 // Loop until told not to
230 while (!consumer_is_stopped(consumer) && terminated == 0) { //
232 // Get a frame from the service
233 if ((frame = mlt_consumer_rt_frame(consumer)) != NULL) {
235 // Check for termination
236 if (terminate_on_pause && frame != NULL) {
237 terminated = mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed") == 0.0;
238 if (terminated == 1) {
239 mlt_log_info(MLT_CONSUMER_SERVICE(consumer), "End of playout reached, terminating\n");
241 consumer_stop(consumer);
245 // True if mlt_consumer_rt_frame(...) successful
246 if (mlt_properties_get_int(mlt_frame_properties(frame), "rendered") == 1) {
247 mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer);
249 // Tell the framework how we want our audio and video
250 int width = mlt_properties_get_int(properties, "width");
251 int height = mlt_properties_get_int(properties, "height");
252 int frequency = 48000;
254 int samples = mlt_sample_calculator(fps, frequency, count++);
255 mlt_image_format pix_fmt = mlt_image_yuv422;
256 mlt_audio_format aformat = mlt_audio_s16;
257 uint8_t *video_buffer;
258 int16_t *audio_buffer_tmp; // the upstream audio buffer
260 // Get the video from this frame and save it to our video_buffer
261 mlt_frame_get_image(frame, &video_buffer, &pix_fmt, &width, &height, 0);
263 // Get the audio from this frame and save it to our audio_buffer
264 mlt_frame_get_audio(frame, (void**) &audio_buffer_tmp, &aformat, &frequency, &channels, &samples);
265 mlt_log_debug(MLT_CONSUMER_SERVICE(consumer), "channels: %i samples: %i\n", channels, samples);
267 int out_channels = channels;
268 if ( mlt_properties_get( properties, "force_channels" ) )
269 out_channels = mlt_properties_get_int( properties, "force_channels" );
271 // Tell the sdi_generator.c to playout our frame
272 // 8 audio streams with 2 stereo channels are possible
275 int map_channels, map_start;
277 for (i = 0; i < MAX_AUDIO_STREAMS && j < out_channels; i++) {
281 sprintf(key, "meta.map.audio.%d.channels", i);
282 map_channels = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), key);
283 sprintf(key, "meta.map.audio.%d.start", i);
284 map_start = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), key);
286 map_channels = channels - j;
287 for (c = 0; c < map_channels && j < channels; c++, j++) {
288 int16_t *src = audio_buffer_tmp + j;
289 int16_t *dest = this->audio_buffer[(map_start + c) / 2] + (map_start + c) % 2;
299 my_dbn = sdimaster_playout(video_buffer, this->audio_buffer, (out_channels + 1) / 2, my_dbn);
301 mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "video_buffer was NULL, skipping playout\n");
304 mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "WARNING the requested frame is not yet rendered! This will cause image disturbance!\n");
308 mlt_frame_close(frame);
315 * Callback to allow override of the close method.
317 static void consumer_close(mlt_consumer parent) {
319 // Get the actual object
320 consumer_SDIstream this = parent->child;
322 free(this->path_destination_sdi);
324 // Now clean up the rest (the close = NULL is a bit nasty but needed for now)
325 parent->close = NULL;
326 mlt_consumer_close(parent);
328 // Invoke the close function of the sdi_generator to close opened files used for output
331 // Finally clean up this