]> git.sesse.net Git - mlt/blob - src/modules/linsys/consumer_SDIstream.c
Fix compile error on Windows.
[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( 1, sizeof(struct consumer_SDIstream_s) );
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 = -1;
363         if (this->device_file_video)
364                 fd = stat(this->device_file_video, &st);
365         if (fd == -1) {
366                 if (this->device_file_video)
367                         free(this->device_file_video);
368                 this->device_file_video = strdup("/dev/sdivideotx0");
369         } else {
370                 close(fd);
371         }
372         if (this->device_file_audio) {
373                 fd = stat(this->device_file_audio, &st);
374                 if (fd == -1) {
375                         if (this->device_file_audio)
376                                 free(this->device_file_audio);
377                         this->device_file_audio = strdup("/dev/sdiaudiotx0");
378                 } else {
379                         close(fd);
380                 }
381         } else if (this->device_file_video &&
382                         strstr(this->device_file_video, "sdivideotx")) {
383                 if (this->device_file_audio)
384                         free(this->device_file_audio);
385                 this->device_file_audio = strdup("/dev/sdiaudiotx0");
386         }
387
388         // set blanking flag; is not nessary we write no own blanking(HANC) for HD board ASSY 193
389         if (mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "blanking")) {
390                 // set value
391                 if (!strcmp( mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "blanking"), "false")) {
392                         this->blanking = 0;
393                 } else if (!strcmp( mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "blanking"), "true")) {
394                         this->blanking = 1;
395                 } else {
396                         this->blanking = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES( consumer ), "blanking");
397                 }
398         } else if (this->device_file_video && strstr(this->device_file_video, "sdivideotx")) {
399                 this->blanking = 0;
400         } else {
401                 // set default value without HD board, also with blanking
402                 this->blanking = 1;
403         }
404
405         // Define a frame pointer
406         mlt_frame frame;
407
408         // set Datablock number for SDI encoding
409         int my_dbn = 1;
410
411         double fps = mlt_properties_get_double(MLT_CONSUMER_PROPERTIES(consumer), "fps");
412         unsigned int count = 0;
413
414         // Tell the framework how we want our audio and video
415         int frequency = this->audio_format.sample_rate;
416         int channels = mlt_properties_get_int(  MLT_CONSUMER_PROPERTIES(consumer), "channels" );
417         int samples;
418
419         // set number of audio channels, linsys vidport model 193 is limited to 8 channels (4AES frames)
420         this->audio_format.channels = 8; /* 0,2,4,6,8 */
421         this->audio_format.aformat = mlt_audio_s16; /* 16, 24, 32 */
422         this->audio_format.sample_rate = 48000;
423         this->pix_fmt = mlt_image_yuv422;
424
425         if (this->device_file_video && this->device_file_audio &&
426                 !sdi_init(this->device_file_video, this->device_file_audio, this->blanking, mlt_service_profile((mlt_service) consumer), &this->audio_format)) {
427                 mlt_log_fatal( MLT_CONSUMER_SERVICE(consumer), "failed to initialize\n" );
428                 mlt_events_fire( MLT_CONSUMER_PROPERTIES(consumer), "consumer-fatal-error", NULL );
429         }
430
431         uint8_t *video_buffer;
432         int16_t *audio_buffer_tmp; // the upstream audio buffer
433
434         // Loop until told not to
435         while (!consumer_is_stopped(consumer) && terminated == 0) { //
436
437                 // Get a frame from the service
438                 if ((frame = mlt_consumer_rt_frame(consumer)) != NULL) {
439
440                         // Check for termination
441                         if (terminate_on_pause && frame != NULL) {
442                                 terminated = mlt_properties_get_double(MLT_FRAME_PROPERTIES( frame ), "_speed") == 0.0;
443                                 if (terminated == 1) {
444                                         mlt_log_verbose(MLT_CONSUMER_SERVICE(consumer), "\nEnd of playout reached, terminating\n");
445                                         consumer_stop(consumer);
446                                 }
447                         }
448
449                         // True if mlt_consumer_rt_frame(...) successful
450                         if (mlt_properties_get_int(mlt_frame_properties(frame), "rendered") == 1) {
451
452                                 // Get the video from this frame and save it to our video_buffer
453                                 mlt_frame_get_image(frame, &video_buffer, &this->pix_fmt, &this->width, &this->height, 1);
454
455                                 // Get the audio from this frame and save it to our audio_buffer
456                                 samples = mlt_sample_calculator(fps, frequency, count++);
457                                 mlt_frame_get_audio(frame, (void**) &audio_buffer_tmp, &this->audio_format.aformat, &frequency, &channels, &samples);
458
459                                 this->audio_format.sample_rate = frequency;
460                                 this->audio_format.samples = samples;
461
462                                 /* TODO: Audio is currently hard coded to 8 channels because write 8 channels to the sdi board.
463                                  The Linys SDI board has to be configured with the same number of channels!
464                                  this->audio_format.channels = channels; // take given number of channels
465                                  */
466
467                                 /* Tell the sdi_generator.c to playout our frame
468                                  * 8 AES (8 x stereo channels are possible, max. 16 channels) Linsys SD board model:    107, 159, 145, 180
469                                  * 4 AES (4 x stereo channels are possible, max. 8 channels) Linsys HD board model:             193
470                                  */
471                                 if (video_buffer) {
472
473                                         // provide mapping of audio channels
474                                         int i, j = 0;
475                                         int map_channels, map_start;
476
477                                         for (i = 0; i < MAX_AUDIO_STREAMS && j < channels; i++) {
478                                                 char key[27];
479                                                 int c;
480
481                                                 sprintf(key, "meta.map.audio.%d.channels", i);
482                                                 map_channels = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), key);
483                                                 sprintf(key, "meta.map.audio.%d.start", i);
484                                                 map_start = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), key);
485
486                                                 if (!map_channels)
487                                                         map_channels = channels - j;
488                                                 for (c = 0; c < map_channels && j < channels; c++, j++) {
489                                                         int16_t *src = audio_buffer_tmp + j;
490                                                         int16_t *dest = this->audio_buffer[(map_start + c) / 2] + (map_start + c) % 2;
491                                                         int s = samples + 1;
492
493                                                         while (--s) {
494                                                                 *dest = *src;
495                                                                 dest += 2;
496                                                                 src += channels;
497                                                         }
498                                                 }
499                                         }
500
501                                         // generate SDI frame and playout
502                                         my_dbn = sdi_playout(video_buffer, this->audio_buffer, &this->audio_format, (channels + 1) / 2, my_dbn);
503
504                                         // write a JPEG of every X-th frame
505                                         if (save_jpegs > 0 && counter >= save_jpegs) {
506                                                 consumer_write_JPEG(jpeg_folder, &video_buffer, mlt_service_profile((mlt_service) consumer));
507                                                 counter = 0;
508                                         } else if (save_jpegs > 0) {
509                                                 counter++;
510                                         }
511                                         mlt_events_fire(MLT_CONSUMER_PROPERTIES( consumer ), "consumer-frame-show", frame, NULL );
512                                 } else {
513                                         mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "Videobuffer was NULL, skipping playout!\n");
514                                 }
515                         } else {
516                                 mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "WARNING the requested frame is not yet rendered! This will cause image disturbance!\n");
517
518                                 if (video_buffer) {
519                                         my_dbn = sdi_playout(video_buffer, this->audio_buffer, &this->audio_format, (channels + 1) / 2, my_dbn);
520                                 } else {
521                                         mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "Videobuffer was NULL, skipping playout!\n");
522                                 }
523                         }
524
525                         if (frame != NULL)
526                                 mlt_frame_close(frame);
527                 }
528
529         }
530         return NULL;
531 }
532
533 /**
534  * Callback to allow override of the close method.
535  **/
536 static void consumer_close(mlt_consumer parent) {
537
538         // Get the actual object
539         consumer_SDIstream this = parent->child;
540
541         free(this->device_file_video);
542         free(this->device_file_audio);
543
544         // Now clean up the rest (the close = NULL is a bit nasty but needed for now)
545         parent->close = NULL;
546         mlt_consumer_close(parent);
547
548         // Invoke the close function of the sdi_generator to close opened files used for output
549         sdimaster_close();
550
551         // Finally clean up this
552         free(this);
553 }
554
555 /**
556  * Write videobuffer as JPEG to path
557  * @param path
558  **/
559 static void consumer_write_JPEG(char * filename, uint8_t **vBuffer, mlt_profile myProfile) {
560
561 #ifdef WITH_JPEG
562
563         int bytes_per_pixel = 3; // or 1 for GRACYSCALE images
564         int color_space = JCS_RGB; // or JCS_GRAYSCALE for grayscale images
565
566         uint8_t * buffer_position = *vBuffer;
567         uint8_t image_rgb[myProfile->width * myProfile->height * bytes_per_pixel];
568
569         //convert vBuffer to RGB
570         int i;
571         for (i = 0; i < sizeof(image_rgb) / 6; i++) {
572                 int y1 = *(buffer_position++);
573                 int cb = *(buffer_position++);
574                 int y2 = *(buffer_position++);
575                 int cr = *(buffer_position++);
576                 convertYCBCRtoRGB(y1, cb, cr, y2, &image_rgb[i * 6]);
577         }
578
579         struct jpeg_compress_struct cinfo;
580         struct jpeg_error_mgr jerr;
581
582         // this is a pointer to one row of image data
583         JSAMPROW row_pointer[1];
584
585         FILE *outfile = fopen(filename, "wb");
586
587         if (!outfile) {
588                 mlt_log_error(NULL, "%s: Error opening output jpeg file %s\n!", __FILE__, filename);
589                 return;
590         }
591         cinfo.err = jpeg_std_error(&jerr);
592         jpeg_create_compress(&cinfo);
593         jpeg_stdio_dest(&cinfo, outfile);
594
595         // Setting the parameters of the output file here
596         cinfo.image_width = myProfile->width;
597         cinfo.image_height = myProfile->height;
598         cinfo.input_components = bytes_per_pixel;
599         cinfo.in_color_space = (J_COLOR_SPACE) color_space;
600
601         // default compression parameters, we shouldn't be worried about these
602         jpeg_set_defaults(&cinfo);
603
604         // Now do the compression
605         jpeg_start_compress(&cinfo, TRUE);
606
607         // like reading a file, this time write one row at a time
608         while (cinfo.next_scanline < cinfo.image_height) {
609                 row_pointer[0] = &image_rgb[cinfo.next_scanline * cinfo.image_width * cinfo.input_components];
610                 jpeg_write_scanlines(&cinfo, row_pointer, 1);
611         }
612         // similar to read file, clean up after we're done compressing
613         jpeg_finish_compress(&cinfo);
614         jpeg_destroy_compress(&cinfo);
615         fclose(outfile);
616 #endif
617 }
618
619 /**
620  * converts  YCbCr samples to two 32-bit RGB values
621  * @param y1 cb cr and y2 values
622  * @param target pointer
623  * @return 0 upon success
624  **/
625 int convertYCBCRtoRGB(int y1, int cb, int cr, int y2, uint8_t * target_rgb) {
626
627 #ifdef WITH_JPEG
628
629         if(y1 > 235)
630                 y1 = 235;
631         if(y1 < 16)
632                 y1 = 16;
633
634         if(y2 > 235)
635                 y2 = 235;
636         if(y2 < 16)
637                 y2 = 16;
638
639         if(cr > 240)
640                 cr = 240;
641         if(cr < 16)
642                 cr = 16;
643
644         if(cb > 240)
645                 cb = 240;
646         if(cb < 16)
647                 cb = 16;
648
649         uint8_t r1, g1, b1, r2, g2, b2;
650
651         //pointer to current output buffer position
652         uint8_t * target_pointer = target_rgb;
653
654         r1 = y1 + 1.402 * (cr - 128);
655         g1 = y1 - 0.34414 * (cb - 128) - 0.71414 * (cr - 128);
656         b1 = y1 + 1.772 * (cb - 128);
657
658         r2 = y2 + 1.402 * (cr - 128);
659         g2 = y2 - 0.34414 * (cb - 128) - 0.71414 *(cr - 128);
660         b2 = y2 + 1.772 * (cb - 128);
661
662         *target_pointer++ = r1;
663         *target_pointer++ = g1;
664         *target_pointer++ = b1;
665
666         *target_pointer++ = r2;
667         *target_pointer++ = g2;
668         *target_pointer++ = b2;
669
670         return 0;
671 #endif
672         return 1;
673 }