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"
74 #define MAX_AUDIO_SAMPLES (1920*2) // 1920 Samples per Channel and we have stereo (48000 Hz /25 fps = 1920 Samples per frame)
75 #define PAL_HEIGHT 576 // only used for MAX_FRAMESIZE
76 #define PAL_WIDTH 720 // only used for MAX_FRAMESIZE
77 #define MAX_FRAMESIZE (PAL_HEIGHT*PAL_WIDTH*2) // SDI frame size, (2 Pixels are represented by 4 bytes, yuyv422)
79 // alias for "struct consumer_SDIstream_s *" , now we can write "consumer_SDIstream". Makes it more readable...
80 typedef struct consumer_SDIstream_s *consumer_SDIstream;
82 struct consumer_SDIstream_s {
84 struct mlt_consumer_s parent; // This is the basic Consumer from which we fetch our data
85 mlt_image_format pix_fmt; // Must be mlt_image_yuv422 for SDI
86 mlt_audio_format aformat; // default: mlt_audio_pcm
92 char *path_destination_sdi; // Path for SDI output
93 uint8_t video_buffer[MAX_FRAMESIZE]; // our picture for this frame
94 int16_t audio_buffer0[MAX_AUDIO_SAMPLES]; // our audio channelpair 1 for this frame
97 /** Forward references to static functions.
100 static int consumer_start(mlt_consumer this);
101 static int consumer_stop(mlt_consumer this);
102 static int consumer_is_stopped(mlt_consumer this);
103 static void consumer_close(mlt_consumer parent);
104 static void *consumer_thread(void *);
106 //*****************************************************************************************************
107 //*****************************************************************************************************
108 //*******************************************SDI Master Consumer***************************************
109 //*****************************************************************************************************
110 //*****************************************************************************************************
114 * This is what will be called by the factory
115 * @param profile: profile name for consumer
116 * @param type: unused
118 * @param *arg: pointer to output path
120 mlt_consumer consumer_SDIstream_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) {
122 // Create the consumer object
123 consumer_SDIstream this = calloc(sizeof(struct consumer_SDIstream_s), 1);
125 // If malloc and consumer init ok
126 if (this != NULL && mlt_consumer_init(&this->parent, this, profile) == 0) {
127 // Get the parent consumer object
128 mlt_consumer parent = &this->parent;
130 // We have stuff to clean up, so override the close method
131 parent->close = consumer_close;
133 // Set output path for SDI, default is "/dev/sditx0"
135 this->path_destination_sdi = strdup("/dev/sditx0");
137 this->path_destination_sdi = strdup(arg);
140 // Set up start/stop/terminated callbacks
141 parent->start = consumer_start;
142 parent->stop = consumer_stop;
143 parent->is_stopped = consumer_is_stopped;
145 // Return the consumer produced
149 // malloc or consumer init failed
157 * Start the consumer.
159 static int consumer_start(mlt_consumer parent) {
160 // Get the properties
161 mlt_properties properties = mlt_consumer_properties(parent);
163 // Get the actual object
164 consumer_SDIstream this = parent->child;
166 // Check that we're not already running
167 if (!mlt_properties_get_int(properties, "running")) {
169 pthread_t *consumer_pthread = calloc(1, sizeof(pthread_t));
171 // Assign the thread to properties
172 mlt_properties_set_data(properties, "consumer_pthread", consumer_pthread, sizeof(pthread_t), free, NULL);
174 // Set the running state
175 mlt_properties_set_int(properties, "running", 1);
177 // Create the the threads
178 pthread_create(consumer_pthread, NULL, consumer_thread, this);
186 static int consumer_stop(mlt_consumer parent) {
188 // Get the properties
189 mlt_properties properties = mlt_consumer_properties(parent);
191 // Check that we're running
192 if (mlt_properties_get_int(properties, "running")) {
194 pthread_t *consumer_pthread = mlt_properties_get_data(properties, "consumer_pthread", NULL);
197 mlt_properties_set_int(properties, "running", 0);
199 // Wait for termination
200 pthread_join(*consumer_pthread, NULL);
206 * Determine if the consumer is stopped.
208 static int consumer_is_stopped(mlt_consumer this) {
209 // Get the properties
210 mlt_properties properties = mlt_consumer_properties(this);
211 return !mlt_properties_get_int(properties, "running");
215 * Threaded wrapper for pipe.
217 static void *consumer_thread(void *arg) {
219 consumer_SDIstream this = arg;
222 mlt_consumer consumer = &this->parent;
224 // Convenience functionality (this is to stop melt/inigo after the end of a playout)
225 int terminate_on_pause = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "terminate_on_pause");
228 // Define a frame pointer
231 if (!sdimaster_init(this->path_destination_sdi, 0)) {
235 // set Datablock number for SDI encoding
238 // Loop until told not to
239 while (!consumer_is_stopped(consumer) && terminated == 0) { //
241 // Get a frame from the service
242 if ((frame = mlt_consumer_rt_frame(consumer)) != NULL) {
244 // Check for termination
245 if (terminate_on_pause && frame != NULL) {
246 terminated = mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed") == 0.0;
247 if (terminated == 1) {
248 printf("\nEnd of playout reached, terminating\n");
250 consumer_stop(consumer);
254 // True if mlt_consumer_rt_frame(...) successful
255 if (mlt_properties_get_int(mlt_frame_properties(frame), "rendered") == 1) {
257 // Get the video from this frame and save it to our video_buffer
258 mlt_frame_get_image(frame, &this->video_buffer, &this->pix_fmt, &this->width, &this->height, 1);
260 // Get the audio from this frame and save it to our audio_buffer
261 //mlt_frame_get_audio(frame, &this->audio_buffer0, &this->aformat, &this->frequency, &this->channels, &this->samples );
262 mlt_frame_get_audio(frame, &this->audio_buffer0, &this->aformat, &this->frequency, &this->channels, &this->samples);
264 //printf("\n &this->channels: %i\n", this->channels);
266 // Tell the sdi_generator.c to playout our frame
267 // 8 audio streams with 2 stereo channels are possible
268 // copy first to all audio stream, because no second or more audio streams are currently available (we think)
269 // TODO struct params with <n> audio buffer and properties (size_of)
270 if (this->video_buffer) {
272 my_dbn = sdimaster_playout(&this->video_buffer, &this->audio_buffer0, &this->audio_buffer0, &this->audio_buffer0, &this->audio_buffer0,
273 &this->audio_buffer0, &this->audio_buffer0, &this->audio_buffer0, &this->audio_buffer0, sizeof(this->video_buffer),
274 sizeof(this->audio_buffer0), 1, my_dbn);
276 printf("SDI-Consumer: Videobuffer was NULL, skipping playout!\n");
279 printf("WARNING the requested frame is not yet rendered! This will cause image disturbance!\n");
283 mlt_frame_close(frame);
290 * Callback to allow override of the close method.
292 static void consumer_close(mlt_consumer parent) {
294 // Get the actual object
295 consumer_SDIstream this = parent->child;
297 free(this->path_destination_sdi);
299 // Now clean up the rest (the close = NULL is a bit nasty but needed for now)
300 parent->close = NULL;
301 mlt_consumer_close(parent);
303 // Invoke the close function of the sdi_generator to close opened files used for output
306 // Finally clean up this