]> git.sesse.net Git - mlt/blob - src/modules/ffmpeg/consumer_ffmpeg.c
11f72b74da26dacc43aa9c7f9cfacf283847c563
[mlt] / src / modules / ffmpeg / consumer_ffmpeg.c
1 /*
2  * consumer_ffmpeg.c -- an ffmpeg consumer
3  * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4  * Author: Charles Yates <charles.yates@pandora.be>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (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 Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include "consumer_ffmpeg.h"
22 #include <framework/mlt_frame.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <pthread.h>
27
28 /** This classes definition.
29 */
30
31 typedef struct consumer_ffmpeg_s *consumer_ffmpeg;
32
33 struct consumer_ffmpeg_s
34 {
35         struct mlt_consumer_s parent;
36         mlt_properties properties;
37         int format;
38         int video;
39         pthread_t thread;
40         int running;
41         uint8_t audio_buffer[ 4096 * 3 ];
42         int audio_avail;
43         pthread_mutex_t audio_mutex;
44         pthread_cond_t audio_cond;
45         int window_width;
46         int window_height;
47         float aspect_ratio;
48         int width;
49         int height;
50         int playing;
51         mlt_frame *queue;
52         int size;
53         int count;
54         uint8_t *buffer;
55 };
56
57 /** Forward references to static functions.
58 */
59
60 static void consumer_close( mlt_consumer parent );
61 static void *consumer_thread( void * );
62
63 /** This is what will be called by the factory - anything can be passed in
64         via the argument, but keep it simple.
65 */
66
67 mlt_consumer consumer_ffmpeg_init( char *arg )
68 {
69         // Create the consumer object
70         consumer_ffmpeg this = calloc( sizeof( struct consumer_ffmpeg_s ), 1 );
71
72         // If no malloc'd and consumer init ok
73         if ( this != NULL && mlt_consumer_init( &this->parent, this ) == 0 )
74         {
75                 // Get the parent consumer object
76                 mlt_consumer parent = &this->parent;
77
78                 // We have stuff to clean up, so override the close method
79                 parent->close = consumer_close;
80
81                 // get a handle on properties
82                 mlt_service service = mlt_consumer_service( parent );
83                 this->properties = mlt_service_properties( service );
84
85                 // This is the initialisation of the consumer
86                 this->running = 1;
87                 pthread_mutex_init( &this->audio_mutex, NULL );
88                 pthread_cond_init( &this->audio_cond, NULL);
89                 
90                 // process actual param
91                 if ( arg == NULL || !strcmp( arg, "-" ) )
92                 {
93                         mlt_properties_set( this->properties, "video_file", "-" );
94                         mlt_properties_set( this->properties, "video_format", "dv" );
95                 }
96                 else
97                 {
98                         mlt_properties_set( this->properties, "video_file", arg );
99                         mlt_properties_set( this->properties, "video_format", "" );
100                 }
101
102                 // Create the the thread
103                 pthread_create( &this->thread, NULL, consumer_thread, this );
104
105                 // Return the consumer produced
106                 return parent;
107         }
108
109         // malloc or consumer init failed
110         free( this );
111
112         // Indicate failure
113         return NULL;
114 }
115
116 static void sdl_fill_audio( void *udata, uint8_t *stream, int len )
117 {
118         consumer_ffmpeg this = udata;
119
120         pthread_mutex_lock( &this->audio_mutex );
121
122         // Block until audio received
123         while ( this->running && len > this->audio_avail )
124                 pthread_cond_wait( &this->audio_cond, &this->audio_mutex );
125
126         if ( this->audio_avail >= len )
127         {
128                 // Remove len from the audio available
129                 this->audio_avail -= len;
130
131                 // Remove the samples
132                 memmove( this->audio_buffer, this->audio_buffer + len, this->audio_avail );
133         }
134         else
135         {
136                 // Just to be safe, wipe the stream first
137                 memset( stream, 0, len );
138
139                 // Copy what we have into the stream
140                 memcpy( stream, this->audio_buffer, this->audio_avail );
141
142                 // No audio left
143                 this->audio_avail = 0;
144         }
145
146         pthread_cond_broadcast( &this->audio_cond );
147         pthread_mutex_unlock( &this->audio_mutex );
148 }
149
150 static int consumer_play_audio( consumer_ffmpeg this, mlt_frame frame, int init_audio )
151 {
152         // Get the properties of this consumer
153         mlt_properties properties = this->properties;
154         mlt_audio_format afmt = mlt_audio_pcm;
155         int channels;
156         int samples;
157         int frequency;
158         int16_t *pcm;
159         int bytes;
160
161         mlt_frame_get_audio( frame, &pcm, &afmt, &frequency, &channels, &samples );
162
163         if ( mlt_properties_get_int( properties, "audio_off" ) )
164                 return init_audio;
165
166         if ( init_audio == 0 )
167         {
168                 bytes = ( samples * channels * 2 );
169                 pthread_mutex_lock( &this->audio_mutex );
170                 while ( bytes > ( sizeof( this->audio_buffer) - this->audio_avail ) )
171                         pthread_cond_wait( &this->audio_cond, &this->audio_mutex );
172                 mlt_properties properties = mlt_frame_properties( frame );
173                 if ( mlt_properties_get_double( properties, "speed" ) == 1 )
174                         memcpy( &this->audio_buffer[ this->audio_avail ], pcm, bytes );
175                 else
176                         memset( &this->audio_buffer[ this->audio_avail ], 0, bytes );
177                 this->audio_avail += bytes;
178                 pthread_cond_broadcast( &this->audio_cond );
179                 pthread_mutex_unlock( &this->audio_mutex );
180         }
181         else
182         {
183                 this->playing = 1;
184         }
185
186         return init_audio;
187 }
188
189 static int consumer_play_video( consumer_ffmpeg this, mlt_frame frame )
190 {
191         // Get the properties of this consumer
192         mlt_properties properties = this->properties;
193
194         if ( mlt_properties_get_int( properties, "video_off" ) )
195         {
196                 mlt_frame_close( frame );
197                 return 0;
198         }
199
200         if ( this->count == this->size )
201         {
202                 this->size += 25;
203                 this->queue = realloc( this->queue, sizeof( mlt_frame ) * this->size );
204         }
205         this->queue[ this->count ++ ] = frame;
206
207         // We're working on the oldest frame now
208         frame = this->queue[ 0 ];
209
210         // Shunt the frames in the queue down
211         int i = 0;
212         for ( i = 1; i < this->count; i ++ )
213                 this->queue[ i - 1 ] = this->queue[ i ];
214         this->count --;
215
216         return 0;
217 }
218
219 /** Threaded wrapper for pipe.
220 */
221
222 static void *consumer_thread( void *arg )
223 {
224         // Identify the arg
225         consumer_ffmpeg this = arg;
226
227         // Get the consumer
228         mlt_consumer consumer = &this->parent;
229
230         // Get the service assoicated to the consumer
231         mlt_service service = mlt_consumer_service( consumer );
232
233         // Define a frame pointer
234         mlt_frame frame;
235
236         // internal intialization
237         int init_audio = 1;
238
239         // Loop until told not to
240         while( this->running )
241         {
242                 // Get a frame from the service (should never return anything other than 0)
243                 if ( mlt_service_get_frame( service, &frame, 0 ) == 0 )
244                 {
245                         init_audio = consumer_play_audio( this, frame, init_audio );
246                         consumer_play_video( this, frame );
247                 }
248         }
249
250         return NULL;
251 }
252
253 /** Callback to allow override of the close method.
254 */
255
256 static void consumer_close( mlt_consumer parent )
257 {
258         // Get the actual object
259         consumer_ffmpeg this = parent->child;
260
261         // Kill the thread and clean up
262         this->running = 0;
263
264         pthread_mutex_lock( &this->audio_mutex );
265         pthread_cond_broadcast( &this->audio_cond );
266         pthread_mutex_unlock( &this->audio_mutex );
267
268         pthread_join( this->thread, NULL );
269         pthread_mutex_destroy( &this->audio_mutex );
270         pthread_cond_destroy( &this->audio_cond );
271                 
272         // Now clean up the rest (the close = NULL is a bit nasty but needed for now)
273         parent->close = NULL;
274         mlt_consumer_close( parent );
275
276         // Finally clean up this
277         free( this );
278 }
279