]> git.sesse.net Git - mlt/blob - src/modules/linsys/consumer_SDIstream.c
f391cb162c3b740706cb4f2631f2a8bec8ff2cdd
[mlt] / src / modules / linsys / consumer_SDIstream.c
1 /*
2  *
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.
5  *
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.
10  *
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.
15  *
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.
19  *
20  *
21  *
22  * DESCRIPTION:
23  * This software act as interface between the MLT Frameworkas as
24  * MLT Consumer and the Linear Systems Ltd. SMPTE 259M-C boards.
25  *
26  * Linear Systems can be contacted at http://www.linsys.ca
27  *
28  * ---------------------------------------------------------------
29  *      System : INTeL I686 64Bit
30  *          OS : Linux SuSE Kernel 2.6.27.23-0.1-default
31  *     Compiler: gcc  (c++)
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:
38  *   /dev/sditx0         (SD)
39  *   /dev/sditx1         (SD)
40  *   /dev/sditx2         (SD)
41  *   /dev/sditx4         (SD)
42  *
43  * Tested with:
44  *      SDI Master™ PCI
45  *      SDI Master™ PCIe
46  *      SDI Master™ FD PCIe LP
47  *      SDI Master™ Quad/o PCIe LP
48  *
49  */
50
51 /*
52  * consumer_SDIstream.c
53  * Consumer for MLT Framework > 0.2.4 (release 0.2.4 does NOT work, profiles where not implemented)
54  *
55  * Example:
56  *      inigo video.dv -consumer linsys_sdi:/dev/sditx0 buffer=0;
57  *      melt video.dv -consumer linsys_sdi:/dev/sditx0 buffer=0;
58  *
59  */
60
61 #include <framework/mlt_frame.h>
62 #include <framework/mlt_consumer.h>
63 #include <framework/mlt_log.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <pthread.h>
68 #include <stdint.h>
69 #include <unistd.h>
70
71 #include "sdi_generator.c"
72
73 typedef struct consumer_SDIstream_s *consumer_SDIstream;
74
75 struct consumer_SDIstream_s {
76
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
80 };
81
82 /** Forward references to static functions.
83  */
84
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 *);
90
91 //*****************************************************************************************************
92 //*****************************************************************************************************
93 //*******************************************SDI Master Consumer***************************************
94 //*****************************************************************************************************
95 //*****************************************************************************************************
96
97
98 /**
99  * This is what will be called by the factory
100  * @param profile: profile name for consumer
101  * @param type: unused
102  * @param *id: unused
103  * @param *arg: pointer to output path
104  **/
105 mlt_consumer consumer_SDIstream_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) {
106
107         // Create the consumer object
108         consumer_SDIstream this = calloc(sizeof(struct consumer_SDIstream_s), 1);
109
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;
114
115                 // We have stuff to clean up, so override the close method
116                 parent->close = consumer_close;
117
118                 // Set output path for SDI, default is "/dev/sditx0"
119                 if (arg == NULL) {
120                         this->path_destination_sdi = strdup("/dev/sditx0");
121                 } else {
122                         this->path_destination_sdi = strdup(arg);
123                 }
124
125                 // Set up start/stop/terminated callbacks
126                 parent->start = consumer_start;
127                 parent->stop = consumer_stop;
128                 parent->is_stopped = consumer_is_stopped;
129
130                 // Hardcode the audio sample rate to 48KHz for SDI
131                 mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(parent), "frequency", 48000);
132
133                 // Return the consumer produced
134                 return parent;
135         }
136
137         // malloc or consumer init failed
138         free(this);
139
140         // Indicate failure
141         return NULL;
142 }
143
144 /**
145  * Start the consumer.
146  **/
147 static int consumer_start(mlt_consumer parent) {
148         // Get the properties
149         mlt_properties properties = mlt_consumer_properties(parent);
150
151         // Get the actual object
152         consumer_SDIstream this = parent->child;
153
154         // Check that we're not already running
155         if (!mlt_properties_get_int(properties, "running")) {
156                 // Allocate threads
157                 pthread_t *consumer_pthread = calloc(1, sizeof(pthread_t));
158
159                 // Assign the thread to properties
160                 mlt_properties_set_data(properties, "consumer_pthread", consumer_pthread, sizeof(pthread_t), free, NULL);
161
162                 // Set the running state
163                 mlt_properties_set_int(properties, "running", 1);
164
165                 // Create the the threads
166                 pthread_create(consumer_pthread, NULL, consumer_thread, this);
167         }
168         return 0;
169 }
170
171 /**
172  * Stop the consumer.
173  **/
174 static int consumer_stop(mlt_consumer parent) {
175
176         // Get the properties
177         mlt_properties properties = mlt_consumer_properties(parent);
178
179         // Check that we're running
180         if (mlt_properties_get_int(properties, "running")) {
181                 // Get the threads
182                 pthread_t *consumer_pthread = mlt_properties_get_data(properties, "consumer_pthread", NULL);
183
184                 // Stop the threads
185                 mlt_properties_set_int(properties, "running", 0);
186
187                 // Wait for termination
188                 pthread_join(*consumer_pthread, NULL);
189         }
190         return 0;
191 }
192
193 /**
194  * Determine if the consumer is stopped.
195  **/
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");
200 }
201
202 /**
203  * Threaded wrapper for pipe.
204  **/
205 static void *consumer_thread(void *arg) {
206         // Identify the arg
207         consumer_SDIstream this = arg;
208
209         // Get the consumer
210         mlt_consumer consumer = &this->parent;
211
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");
214         int terminated = 0;
215
216         // Define a frame pointer
217         mlt_frame frame;
218
219         if (!sdimaster_init(this->path_destination_sdi, 0)) {
220                 exit(0);
221         }
222
223         // set Datablock number for SDI encoding
224         int my_dbn = 1;
225
226         double fps = mlt_properties_get_double(MLT_CONSUMER_PROPERTIES(consumer), "fps");
227         unsigned int count = 0;
228
229         // Loop until told not to
230         while (!consumer_is_stopped(consumer) && terminated == 0) { //
231
232                 // Get a frame from the service
233                 if ((frame = mlt_consumer_rt_frame(consumer)) != NULL) {
234
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");
240                                         fflush(stdout);
241                                         consumer_stop(consumer);
242                                 }
243                         }
244
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);
248
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;
253                                 int channels = 0;
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
259
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);
262
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);
266                                 
267                                 int out_channels = channels;
268                                 if ( mlt_properties_get( properties, "force_channels" ) )
269                                         out_channels = mlt_properties_get_int( properties, "force_channels" );
270
271                                 // Tell the sdi_generator.c to playout our frame
272                                 // 8 audio streams with 2 stereo channels are possible
273                                 if (video_buffer) {
274                                         int i, j = 0;
275                                         int map_channels, map_start;
276
277                                         for (i = 0; i < MAX_AUDIO_STREAMS && j < out_channels; i++) {
278                                                 char key[27];
279                                                 int c;
280
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);
285                                                 if (!map_channels)
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;
290                                                         int s = samples + 1;
291
292                                                         while (--s) {
293                                                                 *dest = *src;
294                                                                 dest += 2;
295                                                                 src += channels;
296                                                         }
297                                                 }
298                                         }
299                                         my_dbn = sdimaster_playout(video_buffer, this->audio_buffer, (out_channels + 1) / 2, my_dbn);
300                                 } else
301                                         mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "video_buffer was NULL, skipping playout\n");
302
303                         } else {
304                                 mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "WARNING the requested frame is not yet rendered! This will cause image disturbance!\n");
305                         }
306
307                         if (frame != NULL)
308                                 mlt_frame_close(frame);
309                 }
310         }
311         return NULL;
312 }
313
314 /**
315  * Callback to allow override of the close method.
316  **/
317 static void consumer_close(mlt_consumer parent) {
318
319         // Get the actual object
320         consumer_SDIstream this = parent->child;
321
322         free(this->path_destination_sdi);
323
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);
327
328         // Invoke the close function of the sdi_generator to close opened files used for output
329         sdimaster_close();
330
331         // Finally clean up this
332         free(this);
333 }