incomplete next/prev clip behaviour
[melted] / 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