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"
73 #define PAL_HEIGHT 576 // only used for MAX_FRAMESIZE
74 #define PAL_WIDTH 720 // only used for MAX_FRAMESIZE
75 #define MAX_FRAMESIZE (PAL_HEIGHT*PAL_WIDTH*2) // SDI frame size, (2 Pixels are represented by 4 bytes, yuyv422)
77 // alias for "struct consumer_SDIstream_s *" , now we can write "consumer_SDIstream". Makes it more readable...
78 typedef struct consumer_SDIstream_s *consumer_SDIstream;
80 struct consumer_SDIstream_s {
82 struct mlt_consumer_s parent; // This is the basic Consumer from which we fetch our data
83 char *path_destination_sdi; // Path for SDI output
84 int16_t audio_buffer[8][MAX_AUDIO_SAMPLES]; // The SDI audio channel pairs for this frame
87 /** Forward references to static functions.
90 static int consumer_start(mlt_consumer this);
91 static int consumer_stop(mlt_consumer this);
92 static int consumer_is_stopped(mlt_consumer this);
93 static void consumer_close(mlt_consumer parent);
94 static void *consumer_thread(void *);
96 //*****************************************************************************************************
97 //*****************************************************************************************************
98 //*******************************************SDI Master Consumer***************************************
99 //*****************************************************************************************************
100 //*****************************************************************************************************
104 * This is what will be called by the factory
105 * @param profile: profile name for consumer
106 * @param type: unused
108 * @param *arg: pointer to output path
110 mlt_consumer consumer_SDIstream_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) {
112 // Create the consumer object
113 consumer_SDIstream this = calloc(sizeof(struct consumer_SDIstream_s), 1);
115 // If malloc and consumer init ok
116 if (this != NULL && mlt_consumer_init(&this->parent, this, profile) == 0) {
117 // Get the parent consumer object
118 mlt_consumer parent = &this->parent;
120 // We have stuff to clean up, so override the close method
121 parent->close = consumer_close;
123 // Set output path for SDI, default is "/dev/sditx0"
125 this->path_destination_sdi = strdup("/dev/sditx0");
127 this->path_destination_sdi = strdup(arg);
130 // Set up start/stop/terminated callbacks
131 parent->start = consumer_start;
132 parent->stop = consumer_stop;
133 parent->is_stopped = consumer_is_stopped;
135 // Hardcode the audio sample rate to 48KHz for SDI
136 mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(parent), "frequency", 48000);
138 // Return the consumer produced
142 // malloc or consumer init failed
150 * Start the consumer.
152 static int consumer_start(mlt_consumer parent) {
153 // Get the properties
154 mlt_properties properties = mlt_consumer_properties(parent);
156 // Get the actual object
157 consumer_SDIstream this = parent->child;
159 // Check that we're not already running
160 if (!mlt_properties_get_int(properties, "running")) {
162 pthread_t *consumer_pthread = calloc(1, sizeof(pthread_t));
164 // Assign the thread to properties
165 mlt_properties_set_data(properties, "consumer_pthread", consumer_pthread, sizeof(pthread_t), free, NULL);
167 // Set the running state
168 mlt_properties_set_int(properties, "running", 1);
170 // Create the the threads
171 pthread_create(consumer_pthread, NULL, consumer_thread, this);
179 static int consumer_stop(mlt_consumer parent) {
181 // Get the properties
182 mlt_properties properties = mlt_consumer_properties(parent);
184 // Check that we're running
185 if (mlt_properties_get_int(properties, "running")) {
187 pthread_t *consumer_pthread = mlt_properties_get_data(properties, "consumer_pthread", NULL);
190 mlt_properties_set_int(properties, "running", 0);
192 // Wait for termination
193 pthread_join(*consumer_pthread, NULL);
199 * Determine if the consumer is stopped.
201 static int consumer_is_stopped(mlt_consumer this) {
202 // Get the properties
203 mlt_properties properties = mlt_consumer_properties(this);
204 return !mlt_properties_get_int(properties, "running");
208 * Threaded wrapper for pipe.
210 static void *consumer_thread(void *arg) {
212 consumer_SDIstream this = arg;
215 mlt_consumer consumer = &this->parent;
217 // Convenience functionality (this is to stop melt/inigo after the end of a playout)
218 int terminate_on_pause = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "terminate_on_pause");
221 // Define a frame pointer
224 if (!sdimaster_init(this->path_destination_sdi, 0)) {
228 // set Datablock number for SDI encoding
231 double fps = mlt_properties_get_double(MLT_CONSUMER_PROPERTIES(consumer), "fps");
232 unsigned int count = 0;
234 // Loop until told not to
235 while (!consumer_is_stopped(consumer) && terminated == 0) { //
237 // Get a frame from the service
238 if ((frame = mlt_consumer_rt_frame(consumer)) != NULL) {
240 // Check for termination
241 if (terminate_on_pause && frame != NULL) {
242 terminated = mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed") == 0.0;
243 if (terminated == 1) {
244 printf("\nEnd of playout reached, terminating\n");
246 consumer_stop(consumer);
250 // True if mlt_consumer_rt_frame(...) successful
251 if (mlt_properties_get_int(mlt_frame_properties(frame), "rendered") == 1) {
252 mlt_properties properties = MLT_CONSUMER_PROPERTIES(consumer);
254 // Tell the framework how we want our audio and video
255 int width = mlt_properties_get_int(properties, "width");
256 int height = mlt_properties_get_int(properties, "height");
257 int frequency = 48000;
259 int samples = mlt_sample_calculator(fps, frequency, count++);
260 mlt_image_format pix_fmt = mlt_image_yuv422;
261 mlt_audio_format aformat = mlt_audio_s16;
262 uint8_t *video_buffer;
263 int16_t *audio_buffer_tmp; // the upstream audio buffer
265 // Get the video from this frame and save it to our video_buffer
266 mlt_frame_get_image(frame, &video_buffer, &pix_fmt, &width, &height, 0);
268 // Get the audio from this frame and save it to our audio_buffer
269 mlt_frame_get_audio(frame, (void**) &audio_buffer_tmp, &aformat, &frequency, &channels, &samples);
271 // printf("\n channels: %i samples: %i\n", channels, samples);
273 // Tell the sdi_generator.c to playout our frame
274 // 8 audio streams with 2 stereo channels are possible
277 for (c = 0; c < channels/2; c++) {
278 int16_t *src = audio_buffer_tmp + c * 2;
279 int16_t *dest = this->audio_buffer[c];
282 memcpy(dest, src, 2 * sizeof(int16_t));
287 my_dbn = sdimaster_playout(video_buffer, this->audio_buffer, sizeof(video_buffer),
288 samples * 2 * sizeof(int16_t), /*channels /*/ 2, my_dbn);
290 printf("SDI-Consumer: Videobuffer was NULL, skipping playout!\n");
293 printf("WARNING the requested frame is not yet rendered! This will cause image disturbance!\n");
297 mlt_frame_close(frame);
304 * Callback to allow override of the close method.
306 static void consumer_close(mlt_consumer parent) {
308 // Get the actual object
309 consumer_SDIstream this = parent->child;
311 free(this->path_destination_sdi);
313 // Now clean up the rest (the close = NULL is a bit nasty but needed for now)
314 parent->close = NULL;
315 mlt_consumer_close(parent);
317 // Invoke the close function of the sdi_generator to close opened files used for output
320 // Finally clean up this