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>
70 #include "sdi_generator.c"
72 typedef struct consumer_SDIstream_s *consumer_SDIstream;
74 struct consumer_SDIstream_s {
76 struct mlt_consumer_s parent; // This is the basic Consumer from which we fetch our data
77 char *path_destination_sdi; // Path for SDI output
78 int16_t audio_buffer[8][MAX_AUDIO_SAMPLES]; // The SDI audio channel pairs for this frame
81 /** Forward references to static functions.
84 static int consumer_start(mlt_consumer this);
85 static int consumer_stop(mlt_consumer this);
86 static int consumer_is_stopped(mlt_consumer this);
87 static void consumer_close(mlt_consumer parent);
88 static void *consumer_thread(void *);
90 //*****************************************************************************************************
91 //*****************************************************************************************************
92 //*******************************************SDI Master Consumer***************************************
93 //*****************************************************************************************************
94 //*****************************************************************************************************
98 * This is what will be called by the factory
99 * @param profile: profile name for consumer
100 * @param type: unused
102 * @param *arg: pointer to output path
104 mlt_consumer consumer_SDIstream_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) {
106 // Create the consumer object
107 consumer_SDIstream this = calloc(sizeof(struct consumer_SDIstream_s), 1);
109 // If malloc and consumer init ok
110 if (this != NULL && mlt_consumer_init(&this->parent, this, profile) == 0) {
111 // Get the parent consumer object
112 mlt_consumer parent = &this->parent;
114 // We have stuff to clean up, so override the close method
115 parent->close = consumer_close;
117 // Set output path for SDI, default is "/dev/sditx0"
119 this->path_destination_sdi = strdup("/dev/sditx0");
121 this->path_destination_sdi = strdup(arg);
124 // Set up start/stop/terminated callbacks
125 parent->start = consumer_start;
126 parent->stop = consumer_stop;
127 parent->is_stopped = consumer_is_stopped;
129 // Hardcode the audio sample rate to 48KHz for SDI
130 mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(parent), "frequency", 48000);
132 // Return the consumer produced
136 // malloc or consumer init failed
144 * Start the consumer.
146 static int consumer_start(mlt_consumer parent) {
147 // Get the properties
148 mlt_properties properties = mlt_consumer_properties(parent);
150 // Get the actual object
151 consumer_SDIstream this = parent->child;
153 // Check that we're not already running
154 if (!mlt_properties_get_int(properties, "running")) {
156 pthread_t *consumer_pthread = calloc(1, sizeof(pthread_t));
158 // Assign the thread to properties
159 mlt_properties_set_data(properties, "consumer_pthread", consumer_pthread, sizeof(pthread_t), free, NULL);
161 // Set the running state
162 mlt_properties_set_int(properties, "running", 1);
164 // Create the the threads
165 pthread_create(consumer_pthread, NULL, consumer_thread, this);
173 static int consumer_stop(mlt_consumer parent) {
175 // Get the properties
176 mlt_properties properties = mlt_consumer_properties(parent);
178 // Check that we're running
179 if (mlt_properties_get_int(properties, "running")) {
181 pthread_t *consumer_pthread = mlt_properties_get_data(properties, "consumer_pthread", NULL);
184 mlt_properties_set_int(properties, "running", 0);
186 // Wait for termination
187 pthread_join(*consumer_pthread, NULL);
193 * Determine if the consumer is stopped.
195 static int consumer_is_stopped(mlt_consumer this) {
196 // Get the properties
197 mlt_properties properties = mlt_consumer_properties(this);
198 return !mlt_properties_get_int(properties, "running");
202 * Threaded wrapper for pipe.
204 static void *consumer_thread(void *arg) {
206 consumer_SDIstream this = arg;
209 mlt_consumer consumer = &this->parent;
211 // Convenience functionality (this is to stop melt/inigo after the end of a playout)
212 int terminate_on_pause = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "terminate_on_pause");
215 // Define a frame pointer
218 if (!sdimaster_init(this->path_destination_sdi, 0)) {
222 // set Datablock number for SDI encoding
225 double fps = mlt_properties_get_double(MLT_CONSUMER_PROPERTIES(consumer), "fps");
226 unsigned int count = 0;
228 // Loop until told not to
229 while (!consumer_is_stopped(consumer) && terminated == 0) { //
231 // Get a frame from the service
232 if ((frame = mlt_consumer_rt_frame(consumer)) != NULL) {
234 // Check for termination
235 if (terminate_on_pause && frame != NULL) {
236 terminated = mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed") == 0.0;
237 if (terminated == 1) {
238 printf("\nEnd of playout reached, terminating\n");
240 consumer_stop(consumer);
244 // True if mlt_consumer_rt_frame(...) successful
245 if (mlt_properties_get_int(mlt_frame_properties(frame), "rendered") == 1) {
246 mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer);
248 // Tell the framework how we want our audio and video
249 int width = mlt_properties_get_int(properties, "width");
250 int height = mlt_properties_get_int(properties, "height");
251 int frequency = 48000;
253 int samples = mlt_sample_calculator(fps, frequency, count++);
254 mlt_image_format pix_fmt = mlt_image_yuv422;
255 mlt_audio_format aformat = mlt_audio_s16;
256 uint8_t *video_buffer;
257 int16_t *audio_buffer_tmp; // the upstream audio buffer
259 // Get the video from this frame and save it to our video_buffer
260 mlt_frame_get_image(frame, &video_buffer, &pix_fmt, &width, &height, 0);
262 // Get the audio from this frame and save it to our audio_buffer
263 mlt_frame_get_audio(frame, (void**) &audio_buffer_tmp, &aformat, &frequency, &channels, &samples);
265 // printf("\n channels: %i samples: %i\n", channels, samples);
267 // Tell the sdi_generator.c to playout our frame
268 // 8 audio streams with 2 stereo channels are possible
271 for (c = 0; c < channels/2; c++) {
272 int16_t *src = audio_buffer_tmp + c * 2;
273 int16_t *dest = this->audio_buffer[c];
276 memcpy(dest, src, 2 * sizeof(int16_t));
281 my_dbn = sdimaster_playout(video_buffer, this->audio_buffer, channels / 2, my_dbn);
283 printf("SDI-Consumer: Videobuffer was NULL, skipping playout!\n");
286 printf("WARNING the requested frame is not yet rendered! This will cause image disturbance!\n");
290 mlt_frame_close(frame);
297 * Callback to allow override of the close method.
299 static void consumer_close(mlt_consumer parent) {
301 // Get the actual object
302 consumer_SDIstream this = parent->child;
304 free(this->path_destination_sdi);
306 // Now clean up the rest (the close = NULL is a bit nasty but needed for now)
307 parent->close = NULL;
308 mlt_consumer_close(parent);
310 // Invoke the close function of the sdi_generator to close opened files used for output
313 // Finally clean up this