]> git.sesse.net Git - mlt/blob - src/modules/linsys/consumer_SDIstream.c
ef4ac37ff07c454551cc723f1fd5c6b4964bd0b1
[mlt] / src / modules / linsys / consumer_SDIstream.c
1 /**
2  *
3  * MLT SDI Consumer:
4  * request video and audio data from MLT and generate an SDI stream
5  *
6  * Copyright (C) Broadcasting Center Europe S.A. http://www.bce.lu
7  * an RTL Group Company  http://www.rtlgroup.com
8  * All rights reserved.
9  *
10  * E-mail: support_plasec@bce.lu
11  *
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
26  *
27  *
28  * DESCRIPTION:
29  * This software act as interface between the MLT Frameworkas as
30  * MLT Consumer and the Linear Systems Ltd. SMPTE 292M and SMPTE 259M-C boards.
31  *
32  * Linear Systems can be contacted at http://www.linsys.ca
33  *
34  **********************************************************************************
35  *      System : INTeL I686 64Bit
36  *          OS : Linux SuSE Kernel 2.6.27.39-0.2-default
37  *     Compiler: gcc 4.3.2 (c++)
38  **********************************************************************************
39  *     Project : MLT SDI Consumer for SD and HD
40  *  Started by : Thomas Kurpick, Dipl.Inf. (FH)
41  **********************************************************************************
42  *  Supported and tested boards for SD-SDI or HD-SDI Output:
43  *      PCI SDI Master™              (model 107)
44  *      PCIe SDI Master™             (model 159)
45  *      PCIe LP SDI Master™ FD       (model 145)
46  *      PCIe LP SDI Master™ Quad/o   (model 180)
47  *      PCIe LP HD-SDI Master™ O     (model 193)
48  *
49  *  Note: PCIe LP HD-SDI Master™ O (model 193) is an VidPort model and supports an
50  *  seperate video and audio interface. Device file:
51  *              /dev/sdivideotx[]       for active video data
52  *      /dev/sdiaudiotx[]       for pcm audio data
53  *
54  *      This mlt consumer use the following device files:
55  *   /dev/sditx[]         (SD-PAL)      up to 8 x AES (8 x stereo / 16 audio channels)
56  *   /dev/sdivideotx[]    (HD)
57  *   /dev/sdiaudiotx[]    (HD)          up to 4 x AES (4 x stereo / 8 audio channels)
58  *
59  *
60  **********************************************************************************
61  * Last modified by:
62  * Thomas Kurpick                     08.Jan.2010
63  * and
64  * Dan Dennedy                        10.Feb.2010
65  * Ver. 2.0
66  * See also Git commit log.
67  *
68  **********************************************************************************
69  *
70  * Consumer properties:
71  *      'dev_video'
72  *      'dev_audio'
73  *      'blanking'
74  * Only to monitor the SDI output a beta version of jpeg-writer is implemented.
75  *      'jpeg_files' a number for output interval
76  *      'save_jpegs' path for image
77  *
78  * EXAMPLE:
79  *
80  * SDI boards with full frame stream (with blanking):
81  *      melt video.dv -consumer sdi:/dev/sditx0
82  *      melt video.dv -consumer sdi:/dev/sditx0 blanking=true
83  *      melt video.dv -consumer sdi dev_video=/dev/sditx0 blanking=true
84  *      melt video.dv audio_index=all -consumer sdi dev_video=/dev/sditx0 blanking=true
85  *
86  * SDI boards without full frame stream (without blanking):
87  *  melt -profile atsc_1080i_50 video.mpeg audio_index=1   -consumer sdi dev_video=/dev/sdivideotx0 dev_audio=/dev/sdiaudiotx0 blanking=false
88  *  melt -profile atsc_1080i_50 video.mpeg audio_index=all -consumer sdi dev_video=/dev/sdivideotx0 dev_audio=/dev/sdiaudiotx0 blanking=false
89  *  melt -profile atsc_1080i_50 video.mpeg audio_index=all -consumer sdi dev_video=/dev/sdivideotx0 dev_audio=/dev/sdiaudiotx0 blanking=false jpeg_files=25 save_jpegs=channel_04.jpg
90  *
91  *
92  * SDI output formats and MLT profiles:
93  * #####################################################################################################################################################
94  * ########## SMPTE 274M 1920 x 1080 Image Sample Structure ############################################################################################
95  * #####################################################################################################################################################
96  * System No.   System nomenclature             Form of scanning        Frame rate                              Embedded Audio                  MLT profile             Linsys board support (model)
97  *      4                       1920 x 1080/60/I                interlaced                      30 HZ                                   4 x AES (8 channels)    atsc_1080i_60   193
98  *      5                       1920 x 1080/59.94/I             interlaced                      30000/1001 ~ 29.97 HZ   4 x AES (8 channels)    atsc_1080i_5994 193
99  *      6                       1920 x 1080/50/I                interlaced                      25 HZ                                   4 x AES (8 channels)    atsc_1080i_50   193
100  *      7                       1920 x 1080/30/P                progressive                     30 HZ                                   4 x AES (8 channels)    atsc_1080p_30   193
101  *      8                       1920 x 1080/29.97/P             progressive                     30000/1001 ~ 29.97 HZ   4 x AES (8 channels)    atsc_1080p_2997 193
102  *      9                       1920 x 1080/25/P                progressive                     25 HZ                                   4 x AES (8 channels)    atsc_1080p_25   193
103  *      10                      1920 x 1080/24/P                progressive                     24 HZ                                   4 x AES (8 channels)    atsc_1080p_24   193
104  *      11                      1920 x 1080/23.98/P             progressive                     24000/1001 ~ 23.98 HZ   4 x AES (8 channels)    atsc_1080p_2398 193
105  *
106  * #####################################################################################################################################################
107  * ########## SMPTE 296M 1280 × 720 Progressive Image Sample Structure #################################################################################
108  * #####################################################################################################################################################
109  * System No.   System nomenclature             Form of scanning        Frame rate                              Embedded Audio                  MLT profile             Linsys board support (model)
110  * 1                            1280 × 720/60          progressive                     60 HZ                                   4 x AES (8 channels)    atsc_720p_60    193
111  * 2                            1280 × 720/59.94       progressive                     60000/1001 ~ 59.97 HZ   4 x AES (8 channels)    atsc_720p_5994  193
112  * 3                            1280 × 720/50          progressive                     50 HZ                                   4 x AES (8 channels)    atsc_720p_50    193
113  * 4                            1280 × 720/30          progressive                     30 HZ                                   4 x AES (8 channels)    atsc_720p_30    193
114  * 5                            1280 × 720/29.97       progressive                     30000/1001 ~ 29.97 HZ   4 x AES (8 channels)    atsc_720p_2997  193
115  * 6                            1280 × 720/25          progressive                     25 HZ                                   4 x AES (8 channels)    atsc_720p_25    193
116  * 7                            1280 × 720/24          progressive                     24 HZ                                   4 x AES (8 channels)    atsc_720p_24    193
117  * 8                            1280 × 720/23.98       progressive                     24000/1001 ~ 23.98 HZ   4 x AES (8 channels)    atsc_720p_2398  193
118  *
119  * #####################################################################################################################################################
120  * ########## SMPTE 125M 486i 29.97Hz & BT.656 576i 25Hz ###############################################################################################
121  * #####################################################################################################################################################
122  * System No.   System nomenclature             Form of scanning        Frame rate                              Embedded Audio                  MLT profile             Linsys board support (model)
123  * SD PAL               720 × 576/50/I                 interlaced                      25 HZ                                   8 x AES (16 channels)   dv_pal                  180,145,159,107
124  * SD PAL               720 × 576/50/I                 interlaced                      25 HZ                                   4 x AES (8 channels)    dv_pal                  193
125  * SD NTSC              720 × 486/59.94/I              interlaced                      30000/1001 ~ 29.97 HZ   8 x AES (16 channels)   sdi_486i_5994   TODO:180,145,159,107
126  * SD NTSC              720 × 486/59.94/I              interlaced                      30000/1001 ~ 29.97 HZ   4 x AES (8 channels)    sdi_486i_5994   193
127  *
128  **/
129
130 #include <framework/mlt_consumer.h>
131 #include <framework/mlt_frame.h>
132 #include <framework/mlt_profile.h>
133 #include <framework/mlt_log.h>
134 #include <framework/mlt_events.h>
135 #include <stdlib.h>
136 #include <string.h>
137 #include <pthread.h>
138 #include <stdint.h>
139 #include <unistd.h>
140 #include <sys/types.h>
141 #include <sys/stat.h>
142
143 #ifdef WITH_JPEG
144 // for JPEG output
145 #include <jpeglib.h>
146 #endif
147
148 #include "sdi_generator.c"
149
150 // alias for "struct consumer_SDIstream_s *" , now we can write "consumer_SDIstream". Makes it more readable...
151 typedef struct consumer_SDIstream_s *consumer_SDIstream;
152
153 struct consumer_SDIstream_s {
154         // Most of these values are set to their defaults by the parent consumer
155         struct mlt_consumer_s parent; // This is the basic Consumer from which we fetch our data
156         mlt_image_format pix_fmt; // Must be mlt_image_yuv422 for SDI
157
158         int width;
159         int height;
160
161         struct audio_format audio_format;
162         /**
163          * device file:
164          *              /dev/sditx0
165          *              /dev/sdivideotx0
166          *              /dev/sdiaudiotx0
167          **/
168         char *device_file_video; // Path for SDI output
169         char *device_file_audio; // Path for exlusive SDI audio output
170
171         /**
172          *  write own HANC (ancillary data) is available for:
173          * SDI board ASSY 193 HD        'blanking=false'
174          * SDI board ASSY 180 SD quad   'blanking=true'
175          * SDI board ASSY 145 SD single 'blanking=true'
176          *
177          * 0=false, 1=true
178          *
179          **/
180         uint8_t blanking;
181
182         // our audio channel pair for this frame
183         int16_t audio_buffer[MAX_AUDIO_STREAMS][MAX_AUDIO_SAMPLES]; // The SDI audio channel pairs for this frame
184
185         char *video_fmt_name; // 1080i25, 1080p25, 576i50, 486i2997, ...
186
187 };
188
189 /**
190  * Forward references to static functions.
191  **/
192 static int consumer_start(mlt_consumer this);
193 static int consumer_stop(mlt_consumer this);
194 static int consumer_is_stopped(mlt_consumer this);
195 static void consumer_close(mlt_consumer parent);
196 static void *consumer_thread(void *);
197
198 static void consumer_write_JPEG(char * path, uint8_t **vBuffer, mlt_profile myProfile);
199 int convertYCBCRtoRGB(int y1, int cb, int cr, int y2, uint8_t * target_rgb);
200
201 /*****************************************************************************************************
202  ****************************************** SDI Master Consumer **************************************
203  *****************************************************************************************************/
204
205 /** This is what will be called by the factory
206  * @param profile: profile name for consumer
207  * @param type: unused
208  * @param *id: unused
209  * @param *arg: pointer to output path
210  **/
211 mlt_consumer consumer_SDIstream_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) {
212
213         // Create the consumer object
214         consumer_SDIstream this = calloc(sizeof(struct consumer_SDIstream_s), 1);
215
216         // If malloc and consumer init ok
217         if (this != NULL && mlt_consumer_init(&this->parent, this, profile) == 0) {
218
219                 // Get the parent consumer object
220                 mlt_consumer parent = &this->parent;
221
222                 // We have stuff to clean up, so override the close method
223                 parent->close = consumer_close;
224
225                 // Set output path for SDI, default is "/dev/sditx0"
226                 if (arg == NULL) {
227                         this->device_file_video = strdup("/dev/sditx0");
228                 } else {
229                         this->device_file_video = strdup(arg);
230                 }
231
232                 // Set up start/stop/terminated callbacks
233                 parent->start = consumer_start;
234                 parent->stop = consumer_stop;
235                 parent->is_stopped = consumer_is_stopped;
236
237                 // Set explicit to zero or other value
238                 int i, j;
239                 for (i = 0; i < MAX_AUDIO_STREAMS; i++) {
240                         for (j = 0; j < MAX_AUDIO_SAMPLES; j++) {
241                                 this->audio_buffer[i][j] = j;
242                         }
243                 }
244                 
245                 mlt_events_register( MLT_CONSUMER_PROPERTIES(parent), "consumer-fatal-error", NULL );
246
247                 // Return the consumer produced
248                 return parent;
249         }
250
251         // malloc or consumer init failed
252         free(this);
253
254         // Indicate failure
255         return NULL;
256 }
257
258 /**
259  * Start the consumer.
260  **/
261 static int consumer_start(mlt_consumer parent) {
262
263         // Get the properties
264         mlt_properties properties = mlt_consumer_properties(parent);
265
266         // Get the actual object
267         consumer_SDIstream this = parent->child;
268
269         // Check that we're not already running
270         if (!mlt_properties_get_int(properties, "running")) {
271                 // Allocate threads
272                 pthread_t *consumer_pthread = calloc(1, sizeof(pthread_t));
273
274                 // Assign the thread to properties
275                 mlt_properties_set_data(properties, "consumer_pthread", consumer_pthread, sizeof(pthread_t), free, NULL);
276
277                 // Set the running state
278                 mlt_properties_set_int(properties, "running", 1);
279
280                 // Create the the threads
281                 pthread_create(consumer_pthread, NULL, consumer_thread, this);
282         }
283         return 0;
284 }
285
286 /**
287  * Stop the consumer
288  **/
289 static int consumer_stop(mlt_consumer parent) {
290
291         // Get the properties
292         mlt_properties properties = mlt_consumer_properties(parent);
293
294         // Check that we're running
295         if (mlt_properties_get_int(properties, "running")) {
296
297                 // Get the threads
298                 pthread_t *consumer_pthread = mlt_properties_get_data(properties, "consumer_pthread", NULL);
299
300                 // Stop the threads
301                 mlt_properties_set_int(properties, "running", 0);
302
303                 // Wait for termination
304                 pthread_join(*consumer_pthread, NULL);
305
306         }
307         return 0;
308 }
309
310 /**
311  * Determine if the consumer is stopped
312  **/
313 static int consumer_is_stopped(mlt_consumer this) {
314         // Get the properties
315         mlt_properties properties = mlt_consumer_properties(this);
316         return !mlt_properties_get_int(properties, "running");
317 }
318
319 /**
320  * Threaded wrapper for pipe.
321  **/
322 static void *consumer_thread(void *arg) {
323
324         // Identify the arg
325         consumer_SDIstream this = arg;
326
327         // Get the consumer
328         mlt_consumer consumer = &this->parent;
329
330         // Convenience functionality (this is to stop melt/inigo after the end of a playout)
331         int terminate_on_pause = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES( consumer ), "terminate_on_pause");
332         int terminated = 0; // save ony status
333
334         int save_jpegs = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES( consumer ), "save_jpegs");
335         char * jpeg_folder = mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "jpeg_file");
336
337         // If no folder is specified, skip jpeg export
338         if (jpeg_folder == NULL) {
339                 save_jpegs = 0;
340         }
341
342         if (save_jpegs > 0)
343                 mlt_log_info(MLT_CONSUMER_SERVICE(consumer), "Saving a JPEG every %i frame.\n", save_jpegs);
344
345         int counter = 0; // each second we save a Jpeg
346
347         // set properties (path) for device files
348         if (mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "dev_video") != NULL) {
349                 this->device_file_video = strdup(mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "dev_video"));
350         }
351         if (mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "dev_audio") != NULL) {
352                 if (this->blanking == 0) {
353                         this->device_file_audio = strdup(mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "dev_audio"));
354                 } else {
355                         // if we write HANC we do not write further audio data
356                         mlt_log_info(MLT_CONSUMER_SERVICE(consumer), "Audio device file is set but will not be used.\n");
357                 }
358         }
359
360         // Set additional device file defaults
361         struct stat st;
362         int fd = stat(this->device_file_video, &st);
363         if (fd == -1) {
364                 if (this->device_file_video)
365                         free(this->device_file_video);
366                 this->device_file_video = strdup("/dev/sdivideotx0");
367         } else {
368                 close(fd);
369         }
370         if (this->device_file_audio) {
371                 fd = stat(this->device_file_audio, &st);
372                 if (fd == -1) {
373                         if (this->device_file_audio)
374                                 free(this->device_file_audio);
375                         this->device_file_audio = strdup("/dev/sdiaudiotx0");
376                 } else {
377                         close(fd);
378                 }
379         } else if (strstr(this->device_file_video, "sdivideotx")) {
380                 if (this->device_file_audio)
381                         free(this->device_file_audio);
382                 this->device_file_audio = strdup("/dev/sdiaudiotx0");
383         }
384
385         // set blanking flag; is not nessary we write no own blanking(HANC) for HD board ASSY 193
386         if (mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "blanking")) {
387                 // set value
388                 if (!strcmp( mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "blanking"), "false")) {
389                         this->blanking = 0;
390                 } else if (!strcmp( mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "blanking"), "true")) {
391                         this->blanking = 1;
392                 } else {
393                         this->blanking = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES( consumer ), "blanking");
394                 }
395         } else if (strstr(this->device_file_video, "sdivideotx")) {
396                 this->blanking = 0;
397         } else {
398                 // set default value without HD board, also with blanking
399                 this->blanking = 1;
400         }
401
402         // Define a frame pointer
403         mlt_frame frame;
404
405         // set Datablock number for SDI encoding
406         int my_dbn = 1;
407
408         double fps = mlt_properties_get_double(MLT_CONSUMER_PROPERTIES(consumer), "fps");
409         unsigned int count = 0;
410
411         // Tell the framework how we want our audio and video
412         int frequency = this->audio_format.sample_rate;
413         int channels = mlt_properties_get_int(  MLT_CONSUMER_PROPERTIES(consumer), "channels" );
414         int samples;
415
416         // set number of audio channels, linsys vidport model 193 is limited to 8 channels (4AES frames)
417         this->audio_format.channels = 8; /* 0,2,4,6,8 */
418         this->audio_format.aformat = mlt_audio_s16; /* 16, 24, 32 */
419         this->audio_format.sample_rate = 48000;
420         this->pix_fmt = mlt_image_yuv422;
421
422         if (!sdi_init(this->device_file_video, this->device_file_audio, this->blanking, mlt_service_profile((mlt_service) consumer), &this->audio_format)) {
423                 mlt_log_fatal( MLT_CONSUMER_SERVICE(consumer), "failed to initialize\n" );
424                 mlt_events_fire( MLT_CONSUMER_PROPERTIES(consumer), "consumer-fatal-error", NULL );
425         }
426
427         uint8_t *video_buffer;
428         int16_t *audio_buffer_tmp; // the upstream audio buffer
429
430         // Loop until told not to
431         while (!consumer_is_stopped(consumer) && terminated == 0) { //
432
433                 // Get a frame from the service
434                 if ((frame = mlt_consumer_rt_frame(consumer)) != NULL) {
435
436                         // Check for termination
437                         if (terminate_on_pause && frame != NULL) {
438                                 terminated = mlt_properties_get_double(MLT_FRAME_PROPERTIES( frame ), "_speed") == 0.0;
439                                 if (terminated == 1) {
440                                         mlt_log_verbose(MLT_CONSUMER_SERVICE(consumer), "\nEnd of playout reached, terminating\n");
441                                         consumer_stop(consumer);
442                                 }
443                         }
444
445                         // True if mlt_consumer_rt_frame(...) successful
446                         if (mlt_properties_get_int(mlt_frame_properties(frame), "rendered") == 1) {
447
448                                 // Get the video from this frame and save it to our video_buffer
449                                 mlt_frame_get_image(frame, &video_buffer, &this->pix_fmt, &this->width, &this->height, 1);
450
451                                 // Get the audio from this frame and save it to our audio_buffer
452                                 samples = mlt_sample_calculator(fps, frequency, count++);
453                                 mlt_frame_get_audio(frame, (void**) &audio_buffer_tmp, &this->audio_format.aformat, &frequency, &channels, &samples);
454
455                                 this->audio_format.sample_rate = frequency;
456                                 this->audio_format.samples = samples;
457
458                                 /* TODO: Audio is currently hard coded to 8 channels because write 8 channels to the sdi board.
459                                  The Linys SDI board has to be configured with the same number of channels!
460                                  this->audio_format.channels = channels; // take given number of channels
461                                  */
462
463                                 /* Tell the sdi_generator.c to playout our frame
464                                  * 8 AES (8 x stereo channels are possible, max. 16 channels) Linsys SD board model:    107, 159, 145, 180
465                                  * 4 AES (4 x stereo channels are possible, max. 8 channels) Linsys HD board model:             193
466                                  */
467                                 if (video_buffer) {
468
469                                         // provide mapping of audio channels
470                                         int i, j = 0;
471                                         int map_channels, map_start;
472
473                                         for (i = 0; i < MAX_AUDIO_STREAMS && j < channels; i++) {
474                                                 char key[27];
475                                                 int c;
476
477                                                 sprintf(key, "meta.map.audio.%d.channels", i);
478                                                 map_channels = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), key);
479                                                 sprintf(key, "meta.map.audio.%d.start", i);
480                                                 map_start = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), key);
481
482                                                 if (!map_channels)
483                                                         map_channels = channels - j;
484                                                 for (c = 0; c < map_channels && j < channels; c++, j++) {
485                                                         int16_t *src = audio_buffer_tmp + j;
486                                                         int16_t *dest = this->audio_buffer[(map_start + c) / 2] + (map_start + c) % 2;
487                                                         int s = samples + 1;
488
489                                                         while (--s) {
490                                                                 *dest = *src;
491                                                                 dest += 2;
492                                                                 src += channels;
493                                                         }
494                                                 }
495                                         }
496
497                                         // generate SDI frame and playout
498                                         my_dbn = sdi_playout(video_buffer, this->audio_buffer, &this->audio_format, (channels + 1) / 2, my_dbn);
499
500                                         // write a JPEG of every X-th frame
501                                         if (save_jpegs > 0 && counter >= save_jpegs) {
502                                                 consumer_write_JPEG(jpeg_folder, &video_buffer, mlt_service_profile((mlt_service) consumer));
503                                                 counter = 0;
504                                         } else if (save_jpegs > 0) {
505                                                 counter++;
506                                         }
507                                         mlt_events_fire(MLT_CONSUMER_PROPERTIES( consumer ), "consumer-frame-show", frame, NULL );
508                                 } else {
509                                         mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "Videobuffer was NULL, skipping playout!\n");
510                                 }
511                         } else {
512                                 mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "WARNING the requested frame is not yet rendered! This will cause image disturbance!\n");
513
514                                 if (video_buffer) {
515                                         my_dbn = sdi_playout(video_buffer, this->audio_buffer, &this->audio_format, (channels + 1) / 2, my_dbn);
516                                 } else {
517                                         mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "Videobuffer was NULL, skipping playout!\n");
518                                 }
519                         }
520
521                         if (frame != NULL)
522                                 mlt_frame_close(frame);
523                 }
524
525         }
526         return NULL;
527 }
528
529 /**
530  * Callback to allow override of the close method.
531  **/
532 static void consumer_close(mlt_consumer parent) {
533
534         // Get the actual object
535         consumer_SDIstream this = parent->child;
536
537         free(this->device_file_video);
538         free(this->device_file_audio);
539
540         // Now clean up the rest (the close = NULL is a bit nasty but needed for now)
541         parent->close = NULL;
542         mlt_consumer_close(parent);
543
544         // Invoke the close function of the sdi_generator to close opened files used for output
545         sdimaster_close();
546
547         // Finally clean up this
548         free(this);
549 }
550
551 /**
552  * Write videobuffer as JPEG to path
553  * @param path
554  **/
555 static void consumer_write_JPEG(char * filename, uint8_t **vBuffer, mlt_profile myProfile) {
556
557 #ifdef WITH_JPEG
558
559         int bytes_per_pixel = 3; // or 1 for GRACYSCALE images
560         int color_space = JCS_RGB; // or JCS_GRAYSCALE for grayscale images
561
562         uint8_t * buffer_position = *vBuffer;
563         uint8_t image_rgb[myProfile->width * myProfile->height * bytes_per_pixel];
564
565         //convert vBuffer to RGB
566         int i;
567         for (i = 0; i < sizeof(image_rgb) / 6; i++) {
568                 int y1 = *(buffer_position++);
569                 int cb = *(buffer_position++);
570                 int y2 = *(buffer_position++);
571                 int cr = *(buffer_position++);
572                 convertYCBCRtoRGB(y1, cb, cr, y2, &image_rgb[i * 6]);
573         }
574
575         struct jpeg_compress_struct cinfo;
576         struct jpeg_error_mgr jerr;
577
578         // this is a pointer to one row of image data
579         JSAMPROW row_pointer[1];
580
581         FILE *outfile = fopen(filename, "wb");
582
583         if (!outfile) {
584                 mlt_log_error(NULL, "%s: Error opening output jpeg file %s\n!", __FILE__, filename);
585                 return;
586         }
587         cinfo.err = jpeg_std_error(&jerr);
588         jpeg_create_compress(&cinfo);
589         jpeg_stdio_dest(&cinfo, outfile);
590
591         // Setting the parameters of the output file here
592         cinfo.image_width = myProfile->width;
593         cinfo.image_height = myProfile->height;
594         cinfo.input_components = bytes_per_pixel;
595         cinfo.in_color_space = (J_COLOR_SPACE) color_space;
596
597         // default compression parameters, we shouldn't be worried about these
598         jpeg_set_defaults(&cinfo);
599
600         // Now do the compression
601         jpeg_start_compress(&cinfo, TRUE);
602
603         // like reading a file, this time write one row at a time
604         while (cinfo.next_scanline < cinfo.image_height) {
605                 row_pointer[0] = &image_rgb[cinfo.next_scanline * cinfo.image_width * cinfo.input_components];
606                 jpeg_write_scanlines(&cinfo, row_pointer, 1);
607         }
608         // similar to read file, clean up after we're done compressing
609         jpeg_finish_compress(&cinfo);
610         jpeg_destroy_compress(&cinfo);
611         fclose(outfile);
612 #endif
613 }
614
615 /**
616  * converts  YCbCr samples to two 32-bit RGB values
617  * @param y1 cb cr and y2 values
618  * @param target pointer
619  * @return 0 upon success
620  **/
621 int convertYCBCRtoRGB(int y1, int cb, int cr, int y2, uint8_t * target_rgb) {
622
623 #ifdef WITH_JPEG
624
625         if(y1 > 235)
626                 y1 = 235;
627         if(y1 < 16)
628                 y1 = 16;
629
630         if(y2 > 235)
631                 y2 = 235;
632         if(y2 < 16)
633                 y2 = 16;
634
635         if(cr > 240)
636                 cr = 240;
637         if(cr < 16)
638                 cr = 16;
639
640         if(cb > 240)
641                 cb = 240;
642         if(cb < 16)
643                 cb = 16;
644
645         uint8_t r1, g1, b1, r2, g2, b2;
646
647         //pointer to current output buffer position
648         uint8_t * target_pointer = target_rgb;
649
650         r1 = y1 + 1.402 * (cr - 128);
651         g1 = y1 - 0.34414 * (cb - 128) - 0.71414 * (cr - 128);
652         b1 = y1 + 1.772 * (cb - 128);
653
654         r2 = y2 + 1.402 * (cr - 128);
655         g2 = y2 - 0.34414 * (cb - 128) - 0.71414 *(cr - 128);
656         b2 = y2 + 1.772 * (cb - 128);
657
658         *target_pointer++ = r1;
659         *target_pointer++ = g1;
660         *target_pointer++ = b1;
661
662         *target_pointer++ = r2;
663         *target_pointer++ = g2;
664         *target_pointer++ = b2;
665
666         return 0;
667 #endif
668         return 1;
669 }