1 /*
2  * producer_libdv.c -- simple libdv test case
3  * Copyright (C) 2003-2014 Meltytech, LLC
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include <framework/mlt_producer.h>
21 #include <framework/mlt_frame.h>
22 #include <framework/mlt_deque.h>
23 #include <framework/mlt_factory.h>
24 #include <framework/mlt_profile.h>
25 
26 #include <pthread.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <libdv/dv.h>
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 
36 #define FRAME_SIZE_525_60 	10 * 150 * 80
37 #define FRAME_SIZE_625_50 	12 * 150 * 80
38 
39 /** To conserve resources, we maintain a stack of dv decoders.
40 */
41 
42 static pthread_mutex_t decoder_lock = PTHREAD_MUTEX_INITIALIZER;
43 static mlt_properties dv_decoders = NULL;
44 
dv_decoder_alloc()45 dv_decoder_t *dv_decoder_alloc( )
46 {
47 	// We'll return a dv_decoder
48 	dv_decoder_t *this = NULL;
49 
50 	// Lock the mutex
51 	pthread_mutex_lock( &decoder_lock );
52 
53 	// Create the properties if necessary
54 	if ( dv_decoders == NULL )
55 	{
56 		// Create the properties
57 		dv_decoders = mlt_properties_new( );
58 
59 		// Create the stack
60 		mlt_properties_set_data( dv_decoders, "stack", mlt_deque_init( ), 0, ( mlt_destructor )mlt_deque_close, NULL );
61 
62 		// Register the properties for clean up
63 		mlt_factory_register_for_clean_up( dv_decoders, ( mlt_destructor )mlt_properties_close );
64 	}
65 
66 	// Now try to obtain a decoder
67 	if ( dv_decoders != NULL )
68 	{
69 		// Obtain the stack
70 		mlt_deque stack = mlt_properties_get_data( dv_decoders, "stack", NULL );
71 
72 		// Pop the top of the stack
73 		this = mlt_deque_pop_back( stack );
74 
75 		// Create a new decoder if none available
76 		if ( this == NULL )
77 		{
78 			// We'll need a unique property ID for this
79 			char label[ 256 ];
80 
81 			// Configure the decoder
82 			this = dv_decoder_new( FALSE, FALSE, FALSE );
83 			this->quality = DV_QUALITY_COLOR | DV_QUALITY_AC_2;
84 			this->audio->arg_audio_emphasis = 2;
85 			dv_set_audio_correction( this, DV_AUDIO_CORRECT_AVERAGE );
86 			dv_set_error_log( this, NULL );
87 
88 			// Register it with the properties to ensure clean up
89 			sprintf( label, "%p", this );
90 			mlt_properties_set_data( dv_decoders, label, this, 0, ( mlt_destructor )dv_decoder_free, NULL );
91 		}
92 	}
93 
94 	// Unlock the mutex
95 	pthread_mutex_unlock( &decoder_lock );
96 
97 	return this;
98 }
99 
dv_decoder_return(dv_decoder_t * this)100 void dv_decoder_return( dv_decoder_t *this )
101 {
102 	// Lock the mutex
103 	pthread_mutex_lock( &decoder_lock );
104 
105 	// Now try to return the decoder
106 	if ( dv_decoders != NULL )
107 	{
108 		// Obtain the stack
109 		mlt_deque stack = mlt_properties_get_data( dv_decoders, "stack", NULL );
110 
111 		// Push it back
112 		mlt_deque_push_back( stack, this );
113 	}
114 
115 	// Unlock the mutex
116 	pthread_mutex_unlock( &decoder_lock );
117 }
118 
119 
120 typedef struct producer_libdv_s *producer_libdv;
121 
122 struct producer_libdv_s
123 {
124 	struct mlt_producer_s parent;
125 	int fd;
126 	int is_pal;
127 	uint64_t file_size;
128 	int frame_size;
129 	long frames_in_file;
130 	mlt_producer alternative;
131 };
132 
133 static int producer_get_frame( mlt_producer parent, mlt_frame_ptr frame, int index );
134 static void producer_close( mlt_producer parent );
135 
136 static int producer_collect_info( producer_libdv this, mlt_profile profile );
137 
producer_libdv_init(mlt_profile profile,mlt_service_type type,const char * id,char * filename)138 mlt_producer producer_libdv_init( mlt_profile profile, mlt_service_type type, const char *id, char *filename )
139 {
140 	producer_libdv this = calloc( 1, sizeof( struct producer_libdv_s ) );
141 
142 	if ( filename != NULL && this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
143 	{
144 		int destroy = 0;
145 		mlt_producer producer = &this->parent;
146 		mlt_properties properties = MLT_PRODUCER_PROPERTIES( producer );
147 
148 		// Set the resource property (required for all producers)
149 		mlt_properties_set( properties, "resource", filename );
150 
151 		// Register transport implementation with the producer
152 		producer->close = ( mlt_destructor )producer_close;
153 
154 		// Register our get_frame implementation with the producer
155 		producer->get_frame = producer_get_frame;
156 
157 		// If we have mov or dv, then we'll use an alternative producer
158 		if ( strchr( filename, '.' ) != NULL && (
159 			 strncasecmp( strrchr( filename, '.' ), ".avi", 4 ) == 0 ||
160 			 strncasecmp( strrchr( filename, '.' ), ".mov", 4 ) == 0 ) )
161 		{
162 			// Load via an alternative mechanism
163 			mlt_profile profile = mlt_service_profile( MLT_PRODUCER_SERVICE( producer ) );
164 			this->alternative = mlt_factory_producer( profile, "kino", filename );
165 
166 			// If it's unavailable, then clean up
167 			if ( this->alternative == NULL )
168 				destroy = 1;
169 			else
170 				mlt_properties_pass( properties, MLT_PRODUCER_PROPERTIES( this->alternative ), "" );
171 			this->is_pal = ( ( int ) mlt_producer_get_fps( producer ) ) == 25;
172 		}
173 		else
174 		{
175 			// Open the file if specified
176 			this->fd = open( filename, O_RDONLY );
177 
178 			// Collect info
179 			if ( this->fd == -1 || !producer_collect_info( this, profile ) )
180 				destroy = 1;
181 		}
182 
183 		// If we couldn't open the file, then destroy it now
184 		if ( destroy )
185 		{
186 			mlt_producer_close( producer );
187 			producer = NULL;
188 		}
189 
190 		// Return the producer
191 		return producer;
192 	}
193 	free( this );
194 	return NULL;
195 }
196 
read_frame(int fd,uint8_t * frame_buf,int * isPAL)197 static int read_frame( int fd, uint8_t* frame_buf, int *isPAL )
198 {
199 	int result = read( fd, frame_buf, FRAME_SIZE_525_60 ) == FRAME_SIZE_525_60;
200 	if ( result )
201 	{
202 		*isPAL = ( frame_buf[3] & 0x80 );
203 		if ( *isPAL )
204 		{
205 			int diff = FRAME_SIZE_625_50 - FRAME_SIZE_525_60;
206 			result = read( fd, frame_buf + FRAME_SIZE_525_60, diff ) == diff;
207 		}
208 	}
209 
210 	return result;
211 }
212 
producer_collect_info(producer_libdv this,mlt_profile profile)213 static int producer_collect_info( producer_libdv this, mlt_profile profile )
214 {
215 	int valid = 0;
216 
217 	uint8_t *dv_data = mlt_pool_alloc( FRAME_SIZE_625_50 );
218 
219 	if ( dv_data != NULL )
220 	{
221 		// Read the first frame
222 		valid = read_frame( this->fd, dv_data, &this->is_pal );
223 
224 		// If it looks like a valid frame, the get stats
225 		if ( valid )
226 		{
227 			double aspect_ratio;
228 
229 			// Get the properties
230 			mlt_properties properties = MLT_PRODUCER_PROPERTIES( &this->parent );
231 
232 			// Get a dv_decoder
233 			dv_decoder_t *dv_decoder = dv_decoder_alloc( );
234 
235 			// Determine the file size
236 			struct stat buf;
237 			fstat( this->fd, &buf );
238 
239 			// Store the file size
240 			this->file_size = buf.st_size;
241 
242 			// Determine the frame size
243 			this->frame_size = this->is_pal ? FRAME_SIZE_625_50 : FRAME_SIZE_525_60;
244 
245 			// Determine the number of frames in the file
246 			this->frames_in_file = this->file_size / this->frame_size;
247 
248 			// Calculate default in/out points
249 			int fps = 1000 * ( this->is_pal ? 25 : ( 30000.0 / 1001.0 ) );
250 			if ( ( int )( mlt_profile_fps( profile ) * 1000 ) == fps )
251 			{
252 				if ( this->frames_in_file > 0 )
253 				{
254 					mlt_properties_set_position( properties, "length", this->frames_in_file );
255 					mlt_properties_set_position( properties, "in", 0 );
256 					mlt_properties_set_position( properties, "out", this->frames_in_file - 1 );
257 				}
258 			}
259 			else
260 			{
261 				valid = 0;
262 			}
263 
264 			// Parse the header for meta info
265 			dv_parse_header( dv_decoder, dv_data );
266 			if ( this->is_pal )
267 			{
268 				if ( dv_format_wide( dv_decoder ) )
269 					aspect_ratio = 64.0 / 45.0;
270 				else
271 					aspect_ratio = 16.0 / 15.0;
272 			}
273 			else
274 			{
275 				if ( dv_format_wide( dv_decoder ) )
276 					aspect_ratio = 32.0 / 27.0;
277 				else
278 					aspect_ratio = 8 / 9;
279 			}
280 			mlt_properties_set_double( properties, "aspect_ratio", aspect_ratio);
281 			mlt_properties_set_int( properties, "meta.media.nb_streams", 2 );
282 			mlt_properties_set_int( properties, "video_index", 0 );
283 			mlt_properties_set( properties, "meta.media.0.stream.type", "video" );
284 			mlt_properties_set( properties, "meta.media.0.codec.name", "dvvideo" );
285 			mlt_properties_set( properties, "meta.media.0.codec.long_name", "DV (Digital Video)" );
286 			mlt_properties_set_int( properties, "audio_index", 1 );
287 			mlt_properties_set( properties, "meta.media.1.stream.type", "audio" );
288 			mlt_properties_set( properties, "meta.media.1.codec.name", "pcm_s16le" );
289 			mlt_properties_set( properties, "meta.media.1.codec.long_name", "signed 16-bit little-endian PCM" );
290 			mlt_properties_set_int( properties, "meta.media.width", 720 );
291 			mlt_properties_set_int( properties, "meta.media.height", this->is_pal ? 576 : 480 );
292 			mlt_properties_set_int( properties, "meta.media.frame_rate_num", this->is_pal? 25 : 30000 );
293 			mlt_properties_set_int( properties, "meta.media.frame_rate_den", this->is_pal?  1 :  1001 );
294 
295 			// Return the decoder
296 			dv_decoder_return( dv_decoder );
297 		}
298 
299 		mlt_pool_release( dv_data );
300 	}
301 
302 	return valid;
303 }
304 
producer_get_image(mlt_frame this,uint8_t ** buffer,mlt_image_format * format,int * width,int * height,int writable)305 static int producer_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
306 {
307 	int pitches[3] = { 0, 0, 0 };
308 	uint8_t *pixels[3] = { NULL, NULL, NULL };
309 
310 	// Get the frames properties
311 	mlt_properties properties = MLT_FRAME_PROPERTIES( this );
312 
313 	// Get a dv_decoder
314 	dv_decoder_t *decoder = dv_decoder_alloc( );
315 
316 	// Get the dv data
317 	uint8_t *dv_data = mlt_properties_get_data( properties, "dv_data", NULL );
318 
319 	// Get and set the quality request
320 	char *quality = mlt_frame_pop_service( this );
321 
322 	if ( quality != NULL )
323 	{
324 		if ( strncmp( quality, "fast", 4 ) == 0 )
325 			decoder->quality = ( DV_QUALITY_COLOR | DV_QUALITY_DC );
326 		else if ( strncmp( quality, "best", 4 ) == 0 )
327 			decoder->quality = ( DV_QUALITY_COLOR | DV_QUALITY_AC_2 );
328 		else
329 			decoder->quality = ( DV_QUALITY_COLOR | DV_QUALITY_AC_1 );
330 	}
331 
332 	// Parse the header for meta info
333 	dv_parse_header( decoder, dv_data );
334 
335 	// Assign width and height according to the frame
336 	*width = 720;
337 	*height = dv_data[ 3 ] & 0x80 ? 576 : 480;
338 
339 	// Extract an image of the format requested
340 	if ( *format != mlt_image_rgb24 )
341 	{
342 		// Allocate an image
343 		uint8_t *image = mlt_pool_alloc( *width * ( *height + 1 ) * 2 );
344 
345 		// Pass to properties for clean up
346 		mlt_frame_set_image( this, image, *width * ( *height + 1 ) * 2, mlt_pool_release );
347 
348 		// Decode the image
349 		pitches[ 0 ] = *width * 2;
350 		pixels[ 0 ] = image;
351 		dv_decode_full_frame( decoder, dv_data, e_dv_color_yuv, pixels, pitches );
352 
353 		// Assign result
354 		*buffer = image;
355 		*format = mlt_image_yuv422;
356 	}
357 	else
358 	{
359 		// Allocate an image
360 		uint8_t *image = mlt_pool_alloc( *width * ( *height + 1 ) * 3 );
361 
362 		// Pass to properties for clean up
363 		mlt_frame_set_image( this, image, *width * ( *height + 1 ) * 3, mlt_pool_release );
364 
365 		// Decode the frame
366 		pitches[ 0 ] = 720 * 3;
367 		pixels[ 0 ] = image;
368 		dv_decode_full_frame( decoder, dv_data, e_dv_color_rgb, pixels, pitches );
369 
370 		// Assign result
371 		*buffer = image;
372 		*format = mlt_image_rgb24;
373 	}
374 
375 	// Return the decoder
376 	dv_decoder_return( decoder );
377 
378 	return 0;
379 }
380 
producer_get_audio(mlt_frame this,void ** buffer,mlt_audio_format * format,int * frequency,int * channels,int * samples)381 static int producer_get_audio( mlt_frame this, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
382 {
383 	int16_t *p;
384 	int i, j;
385 	int16_t *audio_channels[ 4 ];
386 
387 	// Get the frames properties
388 	mlt_properties properties = MLT_FRAME_PROPERTIES( this );
389 
390 	// Get a dv_decoder
391 	dv_decoder_t *decoder = dv_decoder_alloc( );
392 
393 	// Get the dv data
394 	uint8_t *dv_data = mlt_properties_get_data( properties, "dv_data", NULL );
395 
396 	// Parse the header for meta info
397 	dv_parse_header( decoder, dv_data );
398 
399 	// Check that we have audio
400 	if ( decoder->audio->num_channels > 0 )
401 	{
402 		int size = *channels * DV_AUDIO_MAX_SAMPLES * sizeof( int16_t );
403 
404 		// Obtain required values
405 		*frequency = decoder->audio->frequency;
406 		*samples = decoder->audio->samples_this_frame;
407 		*channels = decoder->audio->num_channels;
408 		*format = mlt_audio_s16;
409 
410 		// Create a temporary workspace
411 		for ( i = 0; i < 4; i++ )
412 			audio_channels[ i ] = mlt_pool_alloc( DV_AUDIO_MAX_SAMPLES * sizeof( int16_t ) );
413 
414 		// Create a workspace for the result
415 		*buffer = mlt_pool_alloc( size );
416 
417 		// Pass the allocated audio buffer as a property
418 		mlt_frame_set_audio( this, *buffer, *format, size, mlt_pool_release );
419 
420 		// Decode the audio
421 		dv_decode_full_audio( decoder, dv_data, audio_channels );
422 
423 		// Interleave the audio
424 		p = *buffer;
425 		for ( i = 0; i < *samples; i++ )
426 			for ( j = 0; j < *channels; j++ )
427 				*p++ = audio_channels[ j ][ i ];
428 
429 		// Free the temporary work space
430 		for ( i = 0; i < 4; i++ )
431 			mlt_pool_release( audio_channels[ i ] );
432 	}
433 	else
434 	{
435 		// No audio available on the frame, so get test audio (silence)
436 		mlt_frame_get_audio( this, buffer, format, frequency, channels, samples );
437 	}
438 
439 	// Return the decoder
440 	dv_decoder_return( decoder );
441 
442 	return 0;
443 }
444 
producer_get_frame(mlt_producer producer,mlt_frame_ptr frame,int index)445 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
446 {
447 	// Access the private data
448 	producer_libdv this = producer->child;
449 
450 	// Will carry the frame data
451 	uint8_t *data = NULL;
452 
453 	// Obtain the current frame number
454 	uint64_t position = mlt_producer_frame( producer );
455 
456 	if ( this->alternative == NULL )
457 	{
458 		// Convert timecode to a file position (ensuring that we're on a frame boundary)
459 		uint64_t offset = position * this->frame_size;
460 
461 		// Allocate space
462 		data = mlt_pool_alloc( FRAME_SIZE_625_50 );
463 
464 		// Create an empty frame
465 		*frame = mlt_frame_init( MLT_PRODUCER_SERVICE( producer ) );
466 
467 		// Seek and fetch
468 		if ( this->fd != 0 &&
469 		 	 lseek( this->fd, offset, SEEK_SET ) == offset &&
470 		 	 read_frame( this->fd, data, &this->is_pal ) )
471 		{
472 			// Pass the dv data
473 			mlt_properties_set_data( MLT_FRAME_PROPERTIES( *frame ), "dv_data", data, FRAME_SIZE_625_50, ( mlt_destructor )mlt_pool_release, NULL );
474 		}
475 		else
476 		{
477 			mlt_pool_release( data );
478 			data = NULL;
479 		}
480 	}
481 	else
482 	{
483 		// Seek
484 		mlt_producer_seek( this->alternative, position );
485 
486 		// Fetch
487 		mlt_service_get_frame( MLT_PRODUCER_SERVICE( this->alternative ), frame, 0 );
488 
489 		// Verify
490 		if ( *frame != NULL )
491 			data = mlt_properties_get_data( MLT_FRAME_PROPERTIES( *frame ), "dv_data", NULL );
492 	}
493 
494 	if ( data != NULL )
495 	{
496 		// Get the frames properties
497 		mlt_properties properties = MLT_FRAME_PROPERTIES( *frame );
498 
499 		// Get a dv_decoder
500 		dv_decoder_t *dv_decoder = dv_decoder_alloc( );
501 
502 		mlt_properties_set_int( properties, "test_image", 0 );
503 		mlt_properties_set_int( properties, "test_audio", 0 );
504 
505 		// Update other info on the frame
506 		mlt_properties_set_int( properties, "width", 720 );
507 		mlt_properties_set_int( properties, "height", this->is_pal ? 576 : 480 );
508 		mlt_properties_set_int( properties, "top_field_first", !this->is_pal ? 0 : ( data[ 5 ] & 0x07 ) == 0 ? 0 : 1 );
509 		mlt_properties_set_int( properties, "colorspace", 601 );
510 
511 		// Parse the header for meta info
512 		dv_parse_header( dv_decoder, data );
513 		//mlt_properties_set_int( properties, "progressive", dv_is_progressive( dv_decoder ) );
514 		mlt_properties_set_double( properties, "aspect_ratio",
515 				dv_format_wide( dv_decoder ) ? ( this->is_pal ? 118.0/81.0 : 40.0/33.0 ) : ( this->is_pal ? 59.0/54.0 : 10.0/11.0 ) );
516 
517 
518 		mlt_properties_set_int( properties, "audio_frequency", dv_decoder->audio->frequency );
519 		mlt_properties_set_int( properties, "audio_channels", dv_decoder->audio->num_channels );
520 
521 		// Register audio callback
522 		if ( mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( producer ), "audio_index" ) > 0 )
523 			mlt_frame_push_audio( *frame, producer_get_audio );
524 
525 		if ( mlt_properties_get_int( MLT_PRODUCER_PROPERTIES( producer ), "video_index" ) > -1 )
526 		{
527 			// Push the quality string
528 			mlt_frame_push_service( *frame, mlt_properties_get( MLT_PRODUCER_PROPERTIES( producer ), "quality" ) );
529 
530 			// Push the get_image method on to the stack
531 			mlt_frame_push_get_image( *frame, producer_get_image );
532 		}
533 
534 		// Return the decoder
535 		dv_decoder_return( dv_decoder );
536 	}
537 
538 	// Update timecode on the frame we're creating
539 	if ( *frame != NULL )
540 		mlt_frame_set_position( *frame, mlt_producer_position( producer ) );
541 
542 	// Calculate the next timecode
543 	mlt_producer_prepare_next( producer );
544 
545 	return 0;
546 }
547 
producer_close(mlt_producer parent)548 static void producer_close( mlt_producer parent )
549 {
550 	// Obtain this
551 	producer_libdv this = parent->child;
552 
553 	// Close the file
554 	if ( this->fd > 0 )
555 		close( this->fd );
556 
557 	if ( this->alternative )
558 		mlt_producer_close( this->alternative );
559 
560 	// Close the parent
561 	parent->close = NULL;
562 	mlt_producer_close( parent );
563 
564 	// Free the memory
565 	free( this );
566 }
567