9e2eed0f705f5f5cc36c39b57ca0fdaa7071dcc1
[melted] / src / modules / ffmpeg / producer_ffmpeg.c
1 /*
2 * producer_ffmpeg.c -- simple ffmpeg test case
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 "producer_ffmpeg.h"
22 #include <framework/mlt_frame.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 typedef struct producer_ffmpeg_s *producer_ffmpeg;
27
28 struct producer_ffmpeg_s
29 {
30 struct mlt_producer_s parent;
31 char *command;
32 FILE *video;
33 FILE *audio;
34 uint64_t expected;
35 uint8_t *buffer;
36 int open;
37 int width;
38 int height;
39 };
40
41 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index );
42 static void producer_close( mlt_producer parent );
43
44 mlt_producer producer_ffmpeg_init( char *command )
45 {
46 producer_ffmpeg this = calloc( sizeof( struct producer_ffmpeg_s ), 1 );
47 if ( this != NULL && mlt_producer_init( &this->parent, this ) == 0 )
48 {
49 mlt_producer producer = &this->parent;
50
51 producer->get_frame = producer_get_frame;
52 producer->close = producer_close;
53
54 if ( command != NULL )
55 this->command = strdup( command );
56
57 this->buffer = malloc( 1024 * 1024 );
58
59 return producer;
60 }
61 free( this );
62 return NULL;
63 }
64
65 static int producer_get_image( mlt_frame this, uint8_t **buffer, mlt_image_format *format, int *width, int *height, int writable )
66 {
67 // Get the frames properties
68 mlt_properties properties = mlt_frame_properties( this );
69
70 if ( mlt_properties_get_int( properties, "has_image" ) )
71 {
72 // Get width and height
73 *format = mlt_image_yuv422;
74 *width = mlt_properties_get_int( properties, "width" );
75 *height = mlt_properties_get_int( properties, "height" );
76
77 // Specify format and image
78 *buffer = mlt_properties_get_data( properties, "image", NULL );
79 }
80 else
81 {
82 mlt_frame_get_image( this, buffer, format, width, height, writable );
83 }
84
85 return 0;
86 }
87
88 FILE *producer_ffmpeg_run_video( producer_ffmpeg this )
89 {
90 if ( this->video == NULL && !this->open )
91 {
92 if ( this->command != NULL )
93 {
94 char command[ 1024 ];
95 float fps = mlt_producer_get_fps( &this->parent );
96 float position = mlt_producer_position( &this->parent );
97 sprintf( command, "ffmpeg -i \"%s\" -ss %f -f imagepipe -r %f -f yuv4mpegpipe - 2>/dev/null", this->command, position, fps );
98 this->video = popen( command, "r" );
99 }
100 }
101 return this->video;
102 }
103
104 FILE *producer_ffmpeg_run_audio( producer_ffmpeg this )
105 {
106 if ( this->audio == NULL && !this->open )
107 {
108 if ( this->command != NULL )
109 {
110 char command[ 1024 ];
111 float position = mlt_producer_position( &this->parent );
112 sprintf( command, "ffmpeg -i \"%s\" -ss %f -f s16le -ar 48000 -ac 2 - 2>/dev/null", this->command, position );
113 this->audio = popen( command, "r" );
114 }
115 }
116 return this->audio;
117 }
118
119 static void producer_ffmpeg_position( producer_ffmpeg this, uint64_t requested )
120 {
121 if ( requested != this->expected )
122 {
123 if ( this->video != NULL )
124 pclose( this->video );
125 this->video = NULL;
126 if ( this->audio != NULL )
127 pclose( this->audio );
128 this->audio = NULL;
129
130 // We should not be open now
131 this->open = 0;
132 }
133
134 // This is the next frame we expect
135 this->expected = mlt_producer_frame( &this->parent ) + 1;
136
137 // Open the pipe
138 this->video = producer_ffmpeg_run_video( this );
139
140 // Open the audio pipe
141 this->audio = producer_ffmpeg_run_audio( this );
142
143 // We should be open now
144 this->open = 1;
145 }
146
147 static int producer_get_audio( mlt_frame this, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
148 {
149 // Get the frames properties
150 mlt_properties properties = mlt_frame_properties( this );
151
152 producer_ffmpeg producer = mlt_properties_get_data( properties, "producer_ffmpeg", NULL );
153
154 *frequency = 48000;
155 *channels = 2;
156 *samples = 1920;
157
158 // Size
159 int size = *samples * *channels * 2;
160
161 // Allocate an image
162 *buffer = malloc( size );
163
164 // Read it
165 if ( producer->audio != NULL )
166 {
167 if ( fread( *buffer, size, 1, producer->audio ) != 1 )
168 {
169 pclose( producer->audio );
170 producer->audio = NULL;
171 }
172 }
173 else
174 {
175 memset( *buffer, 0, size );
176 }
177
178 // Pass the data on the frame properties
179 mlt_properties_set_data( properties, "audio", *buffer, size, free, NULL );
180
181 return 0;
182 }
183
184 static int read_ffmpeg_header( producer_ffmpeg this, int *width, int *height )
185 {
186 int count = 0;
187 char temp[ 132 ];
188 FILE *video = this->video;
189
190 if ( fgets( temp, 132, video ) )
191 {
192 if ( strncmp( temp, "FRAME", 5 ) )
193 {
194 if ( strstr( temp, " W" ) != NULL )
195 *width = atoi( strstr( temp, " W" ) + 2 );
196 if ( strstr( temp, " H" ) != NULL )
197 *height = atoi( strstr( temp, " H" ) + 2 );
198 count = 2;
199 fgets( temp, 132, video );
200 this->width = *width;
201 this->height = *height;
202 }
203 else
204 {
205 *width = this->width;
206 *height = this->height;
207 count = 2;
208 }
209 }
210 return count;
211 }
212
213 static int producer_get_frame( mlt_producer producer, mlt_frame_ptr frame, int index )
214 {
215 producer_ffmpeg this = producer->child;
216 int width;
217 int height;
218
219 // Construct a test frame
220 *frame = mlt_frame_init( );
221
222 // Are we at the position expected?
223 producer_ffmpeg_position( this, mlt_producer_frame( producer ) );
224
225 // Get the frames properties
226 mlt_properties properties = mlt_frame_properties( *frame );
227
228 FILE *video = this->video;
229
230 // Read the video
231 if ( video != NULL && read_ffmpeg_header( this, &width, &height ) == 2 )
232 {
233 // Allocate an image
234 uint8_t *image = malloc( width * height * 2 );
235
236 // Read it
237 fread( this->buffer, width * height * 3 / 2, 1, video );
238
239 // Convert it
240 mlt_convert_yuv420p_to_yuv422( this->buffer, width, height, width, image );
241
242 // Pass the data on the frame properties
243 mlt_properties_set_data( properties, "image", image, width * height * 2, free, NULL );
244 mlt_properties_set_int( properties, "width", width );
245 mlt_properties_set_int( properties, "height", height );
246 mlt_properties_set_int( properties, "has_image", 1 );
247
248 // Push the image callback
249 mlt_frame_push_get_image( *frame, producer_get_image );
250 }
251 else
252 {
253 // Clean up
254 if ( this->video != NULL )
255 {
256 pclose( this->video );
257 this->video = NULL;
258 }
259
260 // Push the image callback
261 mlt_frame_push_get_image( *frame, producer_get_image );
262 }
263
264 // Set the audio pipe
265 mlt_properties_set_data( properties, "producer_ffmpeg", this, 0, NULL, NULL );
266
267 // Hmm - register audio callback
268 ( *frame )->get_audio = producer_get_audio;
269
270 // Update timecode on the frame we're creating
271 mlt_frame_set_timecode( *frame, mlt_producer_position( producer ) );
272
273 // Calculate the next timecode
274 mlt_producer_prepare_next( producer );
275
276 return 0;
277 }
278
279 static void producer_close( mlt_producer parent )
280 {
281 producer_ffmpeg this = parent->child;
282 if ( this->video )
283 pclose( this->video );
284 if ( this->audio )
285 pclose( this->audio );
286 free( this->command );
287 parent->close = NULL;
288 mlt_producer_close( parent );
289 free( this );
290 }
291