]> git.sesse.net Git - mlt/blob - src/modules/linsys/consumer_SDIstream.c
Add support for >2 audio channels to Linsys SDI consumer.
[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 <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <pthread.h>
67 #include <stdint.h>
68 #include <unistd.h>
69
70 #include "sdi_generator.c"
71
72 // definitions
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)
76
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;
79
80 struct consumer_SDIstream_s {
81
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
85 };
86
87 /** Forward references to static functions.
88  */
89
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 *);
95
96 //*****************************************************************************************************
97 //*****************************************************************************************************
98 //*******************************************SDI Master Consumer***************************************
99 //*****************************************************************************************************
100 //*****************************************************************************************************
101
102
103 /**
104  * This is what will be called by the factory
105  * @param profile: profile name for consumer
106  * @param type: unused
107  * @param *id: unused
108  * @param *arg: pointer to output path
109  **/
110 mlt_consumer consumer_SDIstream_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) {
111
112         // Create the consumer object
113         consumer_SDIstream this = calloc(sizeof(struct consumer_SDIstream_s), 1);
114
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;
119
120                 // We have stuff to clean up, so override the close method
121                 parent->close = consumer_close;
122
123                 // Set output path for SDI, default is "/dev/sditx0"
124                 if (arg == NULL) {
125                         this->path_destination_sdi = strdup("/dev/sditx0");
126                 } else {
127                         this->path_destination_sdi = strdup(arg);
128                 }
129
130                 // Set up start/stop/terminated callbacks
131                 parent->start = consumer_start;
132                 parent->stop = consumer_stop;
133                 parent->is_stopped = consumer_is_stopped;
134
135                 // Hardcode the audio sample rate to 48KHz for SDI
136                 mlt_properties_set_int(MLT_CONSUMER_PROPERTIES(parent), "frequency", 48000);
137
138                 // Return the consumer produced
139                 return parent;
140         }
141
142         // malloc or consumer init failed
143         free(this);
144
145         // Indicate failure
146         return NULL;
147 }
148
149 /**
150  * Start the consumer.
151  **/
152 static int consumer_start(mlt_consumer parent) {
153         // Get the properties
154         mlt_properties properties = mlt_consumer_properties(parent);
155
156         // Get the actual object
157         consumer_SDIstream this = parent->child;
158
159         // Check that we're not already running
160         if (!mlt_properties_get_int(properties, "running")) {
161                 // Allocate threads
162                 pthread_t *consumer_pthread = calloc(1, sizeof(pthread_t));
163
164                 // Assign the thread to properties
165                 mlt_properties_set_data(properties, "consumer_pthread", consumer_pthread, sizeof(pthread_t), free, NULL);
166
167                 // Set the running state
168                 mlt_properties_set_int(properties, "running", 1);
169
170                 // Create the the threads
171                 pthread_create(consumer_pthread, NULL, consumer_thread, this);
172         }
173         return 0;
174 }
175
176 /**
177  * Stop the consumer.
178  **/
179 static int consumer_stop(mlt_consumer parent) {
180
181         // Get the properties
182         mlt_properties properties = mlt_consumer_properties(parent);
183
184         // Check that we're running
185         if (mlt_properties_get_int(properties, "running")) {
186                 // Get the threads
187                 pthread_t *consumer_pthread = mlt_properties_get_data(properties, "consumer_pthread", NULL);
188
189                 // Stop the threads
190                 mlt_properties_set_int(properties, "running", 0);
191
192                 // Wait for termination
193                 pthread_join(*consumer_pthread, NULL);
194         }
195         return 0;
196 }
197
198 /**
199  * Determine if the consumer is stopped.
200  **/
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");
205 }
206
207 /**
208  * Threaded wrapper for pipe.
209  **/
210 static void *consumer_thread(void *arg) {
211         // Identify the arg
212         consumer_SDIstream this = arg;
213
214         // Get the consumer
215         mlt_consumer consumer = &this->parent;
216
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");
219         int terminated = 0;
220
221         // Define a frame pointer
222         mlt_frame frame;
223
224         if (!sdimaster_init(this->path_destination_sdi, 0)) {
225                 exit(0);
226         }
227
228         // set Datablock number for SDI encoding
229         int my_dbn = 1;
230
231         double fps = mlt_properties_get_double(MLT_CONSUMER_PROPERTIES(consumer), "fps");
232         unsigned int count = 0;
233
234         // Loop until told not to
235         while (!consumer_is_stopped(consumer) && terminated == 0) { //
236
237                 // Get a frame from the service
238                 if ((frame = mlt_consumer_rt_frame(consumer)) != NULL) {
239
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");
245                                         fflush(stdout);
246                                         consumer_stop(consumer);
247                                 }
248                         }
249
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);
253
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;
258                                 int channels = 0;
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
264
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);
267
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);
270
271 //                              printf("\n channels: %i samples: %i\n", channels, samples);
272
273                                 // Tell the sdi_generator.c to playout our frame
274                                 // 8 audio streams with 2 stereo channels are possible
275                                 if (video_buffer) {
276                                         int c;
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];
280                                                 int s = samples + 1;
281                                                 while (--s) {
282                                                         memcpy(dest, src, 2 * sizeof(int16_t));
283                                                         dest += 2;
284                                                         src += channels;
285                                                 }
286                                         }
287                                         my_dbn = sdimaster_playout(video_buffer, this->audio_buffer, sizeof(video_buffer),
288                                                         samples * 2 * sizeof(int16_t), /*channels /*/ 2, my_dbn);
289                                 } else
290                                         printf("SDI-Consumer: Videobuffer was NULL, skipping playout!\n");
291
292                         } else {
293                                 printf("WARNING the requested frame is not yet rendered! This will cause image disturbance!\n");
294                         }
295
296                         if (frame != NULL)
297                                 mlt_frame_close(frame);
298                 }
299         }
300         return NULL;
301 }
302
303 /**
304  * Callback to allow override of the close method.
305  **/
306 static void consumer_close(mlt_consumer parent) {
307
308         // Get the actual object
309         consumer_SDIstream this = parent->child;
310
311         free(this->path_destination_sdi);
312
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);
316
317         // Invoke the close function of the sdi_generator to close opened files used for output
318         sdimaster_close();
319
320         // Finally clean up this
321         free(this);
322 }