]> git.sesse.net Git - mlt/blob - src/modules/linsys/consumer_SDIstream.c
Merge branch 'master' of git://mltframework.org/mlt
[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
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)
78
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;
81
82 struct consumer_SDIstream_s {
83
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
87         int width;
88         int height;
89         int frequency;
90         int samples;
91         int channels;
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
95 };
96
97 /** Forward references to static functions.
98  */
99
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 *);
105
106 //*****************************************************************************************************
107 //*****************************************************************************************************
108 //*******************************************SDI Master Consumer***************************************
109 //*****************************************************************************************************
110 //*****************************************************************************************************
111
112
113 /**
114  * This is what will be called by the factory
115  * @param profile: profile name for consumer
116  * @param type: unused
117  * @param *id: unused
118  * @param *arg: pointer to output path
119  **/
120 mlt_consumer consumer_SDIstream_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) {
121
122         // Create the consumer object
123         consumer_SDIstream this = calloc(sizeof(struct consumer_SDIstream_s), 1);
124
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;
129
130                 // We have stuff to clean up, so override the close method
131                 parent->close = consumer_close;
132
133                 // Set output path for SDI, default is "/dev/sditx0"
134                 if (arg == NULL) {
135                         this->path_destination_sdi = strdup("/dev/sditx0");
136                 } else {
137                         this->path_destination_sdi = strdup(arg);
138                 }
139
140                 // Set up start/stop/terminated callbacks
141                 parent->start = consumer_start;
142                 parent->stop = consumer_stop;
143                 parent->is_stopped = consumer_is_stopped;
144
145                 // Return the consumer produced
146                 return parent;
147         }
148
149         // malloc or consumer init failed
150         free(this);
151
152         // Indicate failure
153         return NULL;
154 }
155
156 /**
157  * Start the consumer.
158  **/
159 static int consumer_start(mlt_consumer parent) {
160         // Get the properties
161         mlt_properties properties = mlt_consumer_properties(parent);
162
163         // Get the actual object
164         consumer_SDIstream this = parent->child;
165
166         // Check that we're not already running
167         if (!mlt_properties_get_int(properties, "running")) {
168                 // Allocate threads
169                 pthread_t *consumer_pthread = calloc(1, sizeof(pthread_t));
170
171                 // Assign the thread to properties
172                 mlt_properties_set_data(properties, "consumer_pthread", consumer_pthread, sizeof(pthread_t), free, NULL);
173
174                 // Set the running state
175                 mlt_properties_set_int(properties, "running", 1);
176
177                 // Create the the threads
178                 pthread_create(consumer_pthread, NULL, consumer_thread, this);
179         }
180         return 0;
181 }
182
183 /**
184  * Stop the consumer.
185  **/
186 static int consumer_stop(mlt_consumer parent) {
187
188         // Get the properties
189         mlt_properties properties = mlt_consumer_properties(parent);
190
191         // Check that we're running
192         if (mlt_properties_get_int(properties, "running")) {
193                 // Get the threads
194                 pthread_t *consumer_pthread = mlt_properties_get_data(properties, "consumer_pthread", NULL);
195
196                 // Stop the threads
197                 mlt_properties_set_int(properties, "running", 0);
198
199                 // Wait for termination
200                 pthread_join(*consumer_pthread, NULL);
201         }
202         return 0;
203 }
204
205 /**
206  * Determine if the consumer is stopped.
207  **/
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");
212 }
213
214 /**
215  * Threaded wrapper for pipe.
216  **/
217 static void *consumer_thread(void *arg) {
218         // Identify the arg
219         consumer_SDIstream this = arg;
220
221         // Get the consumer
222         mlt_consumer consumer = &this->parent;
223
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");
226         int terminated = 0;
227
228         // Define a frame pointer
229         mlt_frame frame;
230
231         if (!sdimaster_init(this->path_destination_sdi, 0)) {
232                 exit(0);
233         }
234
235         // set Datablock number for SDI encoding
236         int my_dbn = 1;
237
238         // Loop until told not to
239         while (!consumer_is_stopped(consumer) && terminated == 0) { //
240
241                 // Get a frame from the service
242                 if ((frame = mlt_consumer_rt_frame(consumer)) != NULL) {
243
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");
249                                         fflush(stdout);
250                                         consumer_stop(consumer);
251                                 }
252                         }
253
254                         // True if mlt_consumer_rt_frame(...) successful
255                         if (mlt_properties_get_int(mlt_frame_properties(frame), "rendered") == 1) {
256
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);
259
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);
263
264                                 //printf("\n &this->channels: %i\n", this->channels);
265
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) {
271
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);
275                                 } else
276                                         printf("SDI-Consumer: Videobuffer was NULL, skipping playout!\n");
277
278                         } else {
279                                 printf("WARNING the requested frame is not yet rendered! This will cause image disturbance!\n");
280                         }
281
282                         if (frame != NULL)
283                                 mlt_frame_close(frame);
284                 }
285         }
286         return NULL;
287 }
288
289 /**
290  * Callback to allow override of the close method.
291  **/
292 static void consumer_close(mlt_consumer parent) {
293
294         // Get the actual object
295         consumer_SDIstream this = parent->child;
296
297         free(this->path_destination_sdi);
298
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);
302
303         // Invoke the close function of the sdi_generator to close opened files used for output
304         sdimaster_close();
305
306         // Finally clean up this
307         free(this);
308 }