rmovie 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ext/rmovie/Makefile +149 -0
- data/ext/rmovie/config.h +3 -0
- data/ext/rmovie/extconf.rb +43 -0
- data/ext/rmovie/mkmf.log +24 -0
- data/ext/rmovie/pre-setup.rb +4 -0
- data/ext/rmovie/quadrupel/Makefile +32 -0
- data/ext/rmovie/quadrupel/TODO +15 -0
- data/ext/rmovie/quadrupel/mkinstalldirs +100 -0
- data/ext/rmovie/quadrupel/qp_frame.c +266 -0
- data/ext/rmovie/quadrupel/qp_frame.h +49 -0
- data/ext/rmovie/quadrupel/qp_movie.c +1179 -0
- data/ext/rmovie/quadrupel/qp_movie.h +81 -0
- data/ext/rmovie/quadrupel/qp_util.c +27 -0
- data/ext/rmovie/quadrupel/qp_util.h +18 -0
- data/ext/rmovie/quadrupel/test.c +60 -0
- data/ext/rmovie/rmovie_frame.c +236 -0
- data/ext/rmovie/rmovie_frame.h +10 -0
- data/ext/rmovie/rmovie_main.c +100 -0
- data/ext/rmovie/rmovie_movie.c +349 -0
- data/ext/rmovie/rmovie_movie.h +28 -0
- data/lib/rmovie/version.rb +45 -0
- data/test/export_movie.rb +7 -0
- data/test/get_frame.rb +10 -0
- data/test/get_frames_reverse.rb +11 -0
- data/test/init_rmovie.rb +7 -0
- data/test/media/1701-D.mov +0 -0
- data/test/media/Ballad of the Sneak.mp3 +0 -0
- data/test/media/irnbru-singsong.mov +0 -0
- data/test/media/test.mov +0 -0
- data/test/pixel_format.rb +10 -0
- data/test/test_rmovie.rb +93 -0
- data/test/to_s.rb +27 -0
- data/test/to_string.rb +19 -0
- metadata +83 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
#ifndef QP_FRAME_H
|
2
|
+
#define QP_FRAME_H
|
3
|
+
|
4
|
+
#include "qp_util.h"
|
5
|
+
|
6
|
+
#include <ffmpeg/avcodec.h>
|
7
|
+
#include <ffmpeg/avformat.h>
|
8
|
+
|
9
|
+
/*
|
10
|
+
* Basic frame manipulation routines. This should remain general enough to
|
11
|
+
* be used in ffmpeg-php or rmovie.
|
12
|
+
*/
|
13
|
+
|
14
|
+
typedef struct {
|
15
|
+
AVFrame *av_frame;
|
16
|
+
int width;
|
17
|
+
int height;
|
18
|
+
int pixel_format;
|
19
|
+
int keyframe;
|
20
|
+
int64_t pts;
|
21
|
+
} qp_frame_context;
|
22
|
+
|
23
|
+
int qp_get_frame_height(qp_frame_context *frame);
|
24
|
+
int qp_get_frame_width(qp_frame_context *frame);
|
25
|
+
const char* qp_get_frame_pixel_format_name(qp_frame_context *frame_ctx);
|
26
|
+
|
27
|
+
/* This should be static */
|
28
|
+
void qp_free_av_frame(AVFrame *av_frame);
|
29
|
+
|
30
|
+
qp_frame_context* qp_alloc_frame_ctx(qp_malloc_t malloc_func);
|
31
|
+
void qp_free_frame_ctx(qp_frame_context *frame_ctx, qp_free_t free_func);
|
32
|
+
|
33
|
+
|
34
|
+
int qp_convert_av_frame(AVFrame **av_frame, int pixel_format,
|
35
|
+
int new_format, int width, int height, int free_buffers);
|
36
|
+
|
37
|
+
int qp_convert_frame(qp_frame_context *frame, int new_fmt);
|
38
|
+
int qp_crop_frame(qp_frame_context *frame,
|
39
|
+
int crop_top, int crop_bottom, int crop_left, int crop_right);
|
40
|
+
int qp_resample_frame(qp_frame_context *frame,
|
41
|
+
int wanted_width, int wanted_height, int crop_top, int crop_bottom,
|
42
|
+
int crop_left, int crop_right);
|
43
|
+
|
44
|
+
int qp_output_frame_as_string(qp_frame_context *frame, unsigned char**);
|
45
|
+
|
46
|
+
double qp_get_pts(qp_frame_context *frame);
|
47
|
+
long qp_frame_is_keyframe(qp_frame_context *frame);
|
48
|
+
|
49
|
+
#endif
|
@@ -0,0 +1,1179 @@
|
|
1
|
+
#include <stdlib.h>
|
2
|
+
#include <string.h>
|
3
|
+
|
4
|
+
#include "qp_util.h"
|
5
|
+
#include "qp_movie.h"
|
6
|
+
#include "qp_frame.h"
|
7
|
+
|
8
|
+
#define LRINT(x) ((long) ((x)+0.5))
|
9
|
+
#define MAX(a,b) ((a) > (b) ? (a) : (b))
|
10
|
+
|
11
|
+
#if LIBAVFORMAT_BUILD > 4628
|
12
|
+
#define GET_CODEC_FIELD(codec, field) codec->field
|
13
|
+
#define GET_CODEC_PTR(codec) codec
|
14
|
+
#else
|
15
|
+
#define GET_CODEC_FIELD(codec, field) codec.field
|
16
|
+
#define GET_CODEC_PTR(codec) &codec
|
17
|
+
#endif
|
18
|
+
|
19
|
+
#define MAX_AUDIO_PACKET_SIZE (128 * 1024)
|
20
|
+
|
21
|
+
/*
|
22
|
+
* TODO: this should return error as a string so that the calling
|
23
|
+
* program can decided how to display it.
|
24
|
+
*/
|
25
|
+
/*
|
26
|
+
static void _php_print_av_error(const char *filename, int err)
|
27
|
+
{
|
28
|
+
char* err_string;
|
29
|
+
switch(err) {
|
30
|
+
case AVERROR_IO:
|
31
|
+
fprintf(stderr, "%s: I/O error.\n", filename);
|
32
|
+
break;
|
33
|
+
case AVERROR_NOMEM:
|
34
|
+
fprintf(stderr, "%s: Not enough memory.\n", filename);
|
35
|
+
break;
|
36
|
+
case AVERROR_NOTSUPP:
|
37
|
+
fprintf(stderr, "%s: Operation not supported.\n", filename);
|
38
|
+
break;
|
39
|
+
|
40
|
+
case AVERROR_NUMEXPECTED:
|
41
|
+
fprintf(stderr, "%s: Incorrect image filename syntax.\n", filename);
|
42
|
+
break;
|
43
|
+
case AVERROR_INVALIDDATA:
|
44
|
+
fprintf(stderr, "%s: Error while parsing header\n", filename);
|
45
|
+
break;
|
46
|
+
case AVERROR_NOFMT:
|
47
|
+
fprintf(stderr, "%s: Unknown format\n", filename);
|
48
|
+
case AVERROR_UNKNOWN:
|
49
|
+
// Fall thru to default case
|
50
|
+
default:
|
51
|
+
fprintf(stderr, "%s: Error while opening file (%d)\n", filename, err);
|
52
|
+
break;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
*/
|
56
|
+
|
57
|
+
int qp_open_movie_file(qp_movie_context *movie_ctx, const char* filename)
|
58
|
+
{
|
59
|
+
int ret;
|
60
|
+
|
61
|
+
if (movie_ctx->fmt_ctx) {
|
62
|
+
av_close_input_file(movie_ctx->fmt_ctx);
|
63
|
+
movie_ctx->fmt_ctx = NULL;
|
64
|
+
}
|
65
|
+
|
66
|
+
/* open the file with generic libav function */
|
67
|
+
ret = av_open_input_file(&(movie_ctx->fmt_ctx), filename, NULL, 0, NULL);
|
68
|
+
|
69
|
+
if (ret) {
|
70
|
+
return ret;
|
71
|
+
}
|
72
|
+
|
73
|
+
/* If not enough info to get the stream parameters, we decode the
|
74
|
+
* first frames to get it. */
|
75
|
+
if (av_find_stream_info(movie_ctx->fmt_ctx)) {
|
76
|
+
/* Don't fail here since this is not a problem for formats like .mov */
|
77
|
+
}
|
78
|
+
return 0;
|
79
|
+
}
|
80
|
+
|
81
|
+
|
82
|
+
void qp_free_movie_ctx(qp_movie_context *movie_ctx, qp_free_t free_func)
|
83
|
+
{
|
84
|
+
int i;
|
85
|
+
qp_free_t free_function = free_func ? free_func : free;
|
86
|
+
|
87
|
+
if (movie_ctx->codec_ctx) {
|
88
|
+
for (i = 0; i < MAX_STREAMS; i++) {
|
89
|
+
if (movie_ctx->codec_ctx[i]) {
|
90
|
+
avcodec_close(movie_ctx->codec_ctx[i]);
|
91
|
+
}
|
92
|
+
movie_ctx->codec_ctx[i] = NULL;
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
if (movie_ctx->fmt_ctx) {
|
97
|
+
av_close_input_file(movie_ctx->fmt_ctx);
|
98
|
+
}
|
99
|
+
|
100
|
+
free_function(movie_ctx);
|
101
|
+
}
|
102
|
+
|
103
|
+
|
104
|
+
qp_movie_context* qp_alloc_movie_ctx(qp_malloc_t malloc_func)
|
105
|
+
{
|
106
|
+
int i;
|
107
|
+
qp_movie_context *movie_ctx = NULL;
|
108
|
+
|
109
|
+
qp_malloc_t malloc_function = malloc_func ? malloc_func : malloc;
|
110
|
+
|
111
|
+
movie_ctx = (qp_movie_context *)malloc_function(sizeof(qp_movie_context));
|
112
|
+
movie_ctx->fmt_ctx = NULL;
|
113
|
+
movie_ctx->frame_number = 0;
|
114
|
+
movie_ctx->mode = QP_FRAME_BASED;
|
115
|
+
|
116
|
+
for (i = 0; i < MAX_STREAMS; i++) {
|
117
|
+
movie_ctx->codec_ctx[i] = NULL;
|
118
|
+
}
|
119
|
+
|
120
|
+
return movie_ctx;
|
121
|
+
}
|
122
|
+
|
123
|
+
|
124
|
+
static int get_stream_index(AVFormatContext *fmt_ctx, int type)
|
125
|
+
{
|
126
|
+
int i;
|
127
|
+
|
128
|
+
for (i = 0; i < fmt_ctx->nb_streams; i++) {
|
129
|
+
if (fmt_ctx->streams[i] &&
|
130
|
+
GET_CODEC_FIELD(fmt_ctx->streams[i]->codec, codec_type) == type) {
|
131
|
+
return i;
|
132
|
+
}
|
133
|
+
}
|
134
|
+
/* stream not found */
|
135
|
+
return -1;
|
136
|
+
}
|
137
|
+
|
138
|
+
|
139
|
+
static AVCodecContext* get_decoder_context(qp_movie_context *movie_ctx, int stream_type)
|
140
|
+
{
|
141
|
+
AVCodec *decoder;
|
142
|
+
int stream_index;
|
143
|
+
|
144
|
+
stream_index = get_stream_index(movie_ctx->fmt_ctx, stream_type);
|
145
|
+
if (stream_index < 0) {
|
146
|
+
return NULL;
|
147
|
+
}
|
148
|
+
|
149
|
+
/* check if the codec for this stream is already open */
|
150
|
+
if (!movie_ctx->codec_ctx[stream_index]) {
|
151
|
+
|
152
|
+
/* find the decoder */
|
153
|
+
decoder = avcodec_find_decoder(GET_CODEC_FIELD(
|
154
|
+
movie_ctx->fmt_ctx->streams[stream_index]->codec,
|
155
|
+
codec_id));
|
156
|
+
|
157
|
+
if (!decoder) {
|
158
|
+
return NULL;
|
159
|
+
}
|
160
|
+
|
161
|
+
movie_ctx->codec_ctx[stream_index] =
|
162
|
+
GET_CODEC_PTR(movie_ctx->fmt_ctx->streams[stream_index]->codec);
|
163
|
+
|
164
|
+
/* open the decoder */
|
165
|
+
if (avcodec_open(movie_ctx->codec_ctx[stream_index], decoder) < 0) {
|
166
|
+
return NULL;
|
167
|
+
}
|
168
|
+
}
|
169
|
+
return movie_ctx->codec_ctx[stream_index];
|
170
|
+
}
|
171
|
+
|
172
|
+
|
173
|
+
static AVStream *get_video_stream(AVFormatContext *fmt_ctx)
|
174
|
+
{
|
175
|
+
int i = get_stream_index(fmt_ctx, CODEC_TYPE_VIDEO);
|
176
|
+
|
177
|
+
return i < 0 ? NULL : fmt_ctx->streams[i];
|
178
|
+
}
|
179
|
+
|
180
|
+
|
181
|
+
static AVStream *get_audio_stream(AVFormatContext *fmt_ctx)
|
182
|
+
{
|
183
|
+
int i = get_stream_index(fmt_ctx, CODEC_TYPE_AUDIO);
|
184
|
+
|
185
|
+
return i < 0 ? NULL : fmt_ctx->streams[i];
|
186
|
+
}
|
187
|
+
|
188
|
+
|
189
|
+
float qp_get_frame_rate(qp_movie_context *movie_ctx)
|
190
|
+
{
|
191
|
+
AVStream *st = get_video_stream(movie_ctx->fmt_ctx);
|
192
|
+
float rate = 0.0f;
|
193
|
+
|
194
|
+
if (!st) {
|
195
|
+
return rate;
|
196
|
+
}
|
197
|
+
|
198
|
+
#if LIBAVCODEC_BUILD > 4753
|
199
|
+
if (GET_CODEC_FIELD(st->codec, codec_type) == CODEC_TYPE_VIDEO){
|
200
|
+
if (st->r_frame_rate.den && st->r_frame_rate.num) {
|
201
|
+
rate = av_q2d(st->r_frame_rate);
|
202
|
+
} else {
|
203
|
+
rate = 1 / av_q2d(GET_CODEC_FIELD(st->codec, time_base));
|
204
|
+
}
|
205
|
+
}
|
206
|
+
return (float)rate;
|
207
|
+
#else
|
208
|
+
return (float)GET_CODEC_FIELD(st->codec, frame_rate) /
|
209
|
+
GET_CODEC_FIELD(st->codec, frame_rate_base);
|
210
|
+
#endif
|
211
|
+
}
|
212
|
+
|
213
|
+
|
214
|
+
float qp_get_duration(qp_movie_context *movie_ctx)
|
215
|
+
{
|
216
|
+
float duration;
|
217
|
+
|
218
|
+
duration = (float)movie_ctx->fmt_ctx->duration / AV_TIME_BASE;
|
219
|
+
|
220
|
+
if (duration < 0) {
|
221
|
+
duration = 0.0f;
|
222
|
+
}
|
223
|
+
return duration;
|
224
|
+
}
|
225
|
+
|
226
|
+
|
227
|
+
long qp_get_frame_count(qp_movie_context *movie_ctx)
|
228
|
+
{
|
229
|
+
/* does this movie have a video stream? */
|
230
|
+
if (!get_video_stream(movie_ctx->fmt_ctx)) {
|
231
|
+
return 0;
|
232
|
+
}
|
233
|
+
|
234
|
+
return LRINT(qp_get_frame_rate(movie_ctx) *
|
235
|
+
qp_get_duration(movie_ctx));
|
236
|
+
}
|
237
|
+
|
238
|
+
|
239
|
+
char* qp_get_file_name(qp_movie_context *movie_ctx)
|
240
|
+
{
|
241
|
+
return movie_ctx->fmt_ctx->filename;
|
242
|
+
}
|
243
|
+
|
244
|
+
|
245
|
+
char* qp_get_comment(qp_movie_context *movie_ctx)
|
246
|
+
{
|
247
|
+
return movie_ctx->fmt_ctx->comment;
|
248
|
+
}
|
249
|
+
|
250
|
+
|
251
|
+
char* qp_get_title(qp_movie_context *movie_ctx)
|
252
|
+
{
|
253
|
+
return movie_ctx->fmt_ctx->title;
|
254
|
+
}
|
255
|
+
|
256
|
+
|
257
|
+
char* qp_get_author(qp_movie_context *movie_ctx)
|
258
|
+
{
|
259
|
+
return movie_ctx->fmt_ctx->author;
|
260
|
+
}
|
261
|
+
|
262
|
+
|
263
|
+
char* qp_get_copyright(qp_movie_context *movie_ctx)
|
264
|
+
{
|
265
|
+
return movie_ctx->fmt_ctx->author;
|
266
|
+
}
|
267
|
+
|
268
|
+
|
269
|
+
char* qp_get_album(qp_movie_context *movie_ctx)
|
270
|
+
{
|
271
|
+
return movie_ctx->fmt_ctx->album;
|
272
|
+
}
|
273
|
+
|
274
|
+
|
275
|
+
char* qp_get_genre(qp_movie_context *movie_ctx)
|
276
|
+
{
|
277
|
+
return movie_ctx->fmt_ctx->genre;
|
278
|
+
}
|
279
|
+
|
280
|
+
|
281
|
+
int qp_get_year(qp_movie_context *movie_ctx)
|
282
|
+
{
|
283
|
+
return movie_ctx->fmt_ctx->year;
|
284
|
+
}
|
285
|
+
|
286
|
+
|
287
|
+
int qp_get_track(qp_movie_context *movie_ctx)
|
288
|
+
{
|
289
|
+
return movie_ctx->fmt_ctx->track;
|
290
|
+
}
|
291
|
+
|
292
|
+
|
293
|
+
int qp_get_movie_width(qp_movie_context *movie_ctx)
|
294
|
+
{
|
295
|
+
AVStream *st = get_video_stream(movie_ctx->fmt_ctx);
|
296
|
+
|
297
|
+
if (!st) {
|
298
|
+
return 0;
|
299
|
+
}
|
300
|
+
|
301
|
+
return GET_CODEC_FIELD(st->codec, width);
|
302
|
+
}
|
303
|
+
|
304
|
+
|
305
|
+
int qp_get_movie_height(qp_movie_context *movie_ctx)
|
306
|
+
{
|
307
|
+
AVStream *st = get_video_stream(movie_ctx->fmt_ctx);
|
308
|
+
|
309
|
+
if (!st) {
|
310
|
+
return 0;
|
311
|
+
}
|
312
|
+
|
313
|
+
return GET_CODEC_FIELD(st->codec, height);
|
314
|
+
}
|
315
|
+
|
316
|
+
|
317
|
+
long qp_get_current_frame_number(qp_movie_context *movie_ctx)
|
318
|
+
{
|
319
|
+
AVCodecContext *decoder_ctx = NULL;
|
320
|
+
|
321
|
+
decoder_ctx = get_decoder_context(movie_ctx, CODEC_TYPE_VIDEO);
|
322
|
+
if (!decoder_ctx) {
|
323
|
+
return 0;
|
324
|
+
}
|
325
|
+
|
326
|
+
if (movie_ctx->frame_number <= 0) {
|
327
|
+
return 1; /* no frames read yet so return the first */
|
328
|
+
} else {
|
329
|
+
return movie_ctx->frame_number;
|
330
|
+
}
|
331
|
+
}
|
332
|
+
|
333
|
+
|
334
|
+
static int get_format_number(qp_movie_context *movie_ctx, int codec_type)
|
335
|
+
{
|
336
|
+
AVCodecContext *decoder_ctx = NULL;
|
337
|
+
|
338
|
+
decoder_ctx = get_decoder_context(movie_ctx, codec_type);
|
339
|
+
|
340
|
+
return decoder_ctx ? decoder_ctx->pix_fmt : 0;
|
341
|
+
}
|
342
|
+
|
343
|
+
|
344
|
+
const char* qp_get_movie_pixel_format_name(qp_movie_context *movie_ctx)
|
345
|
+
{
|
346
|
+
int pix_fmt;
|
347
|
+
const char *fmt = NULL;
|
348
|
+
|
349
|
+
pix_fmt = get_format_number(movie_ctx, CODEC_TYPE_VIDEO);
|
350
|
+
fmt = avcodec_get_pix_fmt_name(pix_fmt);
|
351
|
+
|
352
|
+
if (fmt) {
|
353
|
+
return fmt;
|
354
|
+
} else {
|
355
|
+
return NULL;
|
356
|
+
}
|
357
|
+
}
|
358
|
+
|
359
|
+
|
360
|
+
int qp_get_bit_rate(qp_movie_context *movie_ctx)
|
361
|
+
{
|
362
|
+
return movie_ctx->fmt_ctx->bit_rate;
|
363
|
+
}
|
364
|
+
|
365
|
+
|
366
|
+
const char* qp_get_codec_name(qp_movie_context *movie_ctx, int type)
|
367
|
+
{
|
368
|
+
AVCodecContext *decoder_ctx = NULL;
|
369
|
+
AVCodec *p = NULL;
|
370
|
+
const char *codec_name;
|
371
|
+
char buf1[32];
|
372
|
+
|
373
|
+
if (!qp_has_video(movie_ctx)) {
|
374
|
+
return NULL;
|
375
|
+
}
|
376
|
+
|
377
|
+
decoder_ctx = get_decoder_context(movie_ctx, type);
|
378
|
+
if (!decoder_ctx) {
|
379
|
+
return NULL;
|
380
|
+
}
|
381
|
+
|
382
|
+
p = avcodec_find_decoder(decoder_ctx->codec_id);
|
383
|
+
|
384
|
+
/* Copied from libavcodec/utils.c::avcodec_string */
|
385
|
+
if (p) {
|
386
|
+
codec_name = p->name;
|
387
|
+
if (decoder_ctx->codec_id == CODEC_ID_MP3) {
|
388
|
+
if (decoder_ctx->sub_id == 2)
|
389
|
+
codec_name = "mp2";
|
390
|
+
else if (decoder_ctx->sub_id == 1)
|
391
|
+
codec_name = "mp1";
|
392
|
+
}
|
393
|
+
} else if (decoder_ctx->codec_id == CODEC_ID_MPEG2TS) {
|
394
|
+
/* fake mpeg2 transport stream codec (currently not registered) */
|
395
|
+
codec_name = "mpeg2ts";
|
396
|
+
} else if (decoder_ctx->codec_name[0] != '\0') {
|
397
|
+
codec_name = decoder_ctx->codec_name;
|
398
|
+
} else {
|
399
|
+
/* output avi tags */
|
400
|
+
if (decoder_ctx->codec_type == CODEC_TYPE_VIDEO) {
|
401
|
+
snprintf(buf1, sizeof(buf1), "%c%c%c%c",
|
402
|
+
decoder_ctx->codec_tag & 0xff,
|
403
|
+
(decoder_ctx->codec_tag >> 8) & 0xff,
|
404
|
+
(decoder_ctx->codec_tag >> 16) & 0xff,
|
405
|
+
(decoder_ctx->codec_tag >> 24) & 0xff);
|
406
|
+
} else {
|
407
|
+
snprintf(buf1, sizeof(buf1), "0x%04x", decoder_ctx->codec_tag);
|
408
|
+
}
|
409
|
+
codec_name = buf1;
|
410
|
+
}
|
411
|
+
|
412
|
+
return codec_name;
|
413
|
+
}
|
414
|
+
|
415
|
+
|
416
|
+
int qp_has_audio(qp_movie_context *movie_ctx)
|
417
|
+
{
|
418
|
+
return get_audio_stream(movie_ctx->fmt_ctx) ? 1 : 0;
|
419
|
+
}
|
420
|
+
|
421
|
+
|
422
|
+
int qp_has_video(qp_movie_context *movie_ctx)
|
423
|
+
{
|
424
|
+
return get_video_stream(movie_ctx->fmt_ctx) ? 1 : 0;
|
425
|
+
}
|
426
|
+
|
427
|
+
|
428
|
+
int qp_get_num_audio_channels(qp_movie_context *movie_ctx)
|
429
|
+
{
|
430
|
+
AVCodecContext *decoder_ctx = NULL;
|
431
|
+
|
432
|
+
if (!qp_has_audio(movie_ctx)) {
|
433
|
+
return 0;
|
434
|
+
}
|
435
|
+
|
436
|
+
decoder_ctx = get_decoder_context(movie_ctx, CODEC_TYPE_AUDIO);
|
437
|
+
if (!decoder_ctx) {
|
438
|
+
return 0;
|
439
|
+
}
|
440
|
+
|
441
|
+
return decoder_ctx->channels;
|
442
|
+
}
|
443
|
+
|
444
|
+
|
445
|
+
int qp_get_codec_sample_rate(qp_movie_context *movie_ctx, int type)
|
446
|
+
{
|
447
|
+
AVCodecContext *decoder_ctx = NULL;
|
448
|
+
|
449
|
+
decoder_ctx = get_decoder_context(movie_ctx, type);
|
450
|
+
if (!decoder_ctx) {
|
451
|
+
return 0;
|
452
|
+
}
|
453
|
+
|
454
|
+
return decoder_ctx->sample_rate;
|
455
|
+
}
|
456
|
+
|
457
|
+
/* Returns the bit rate for codec of type. */
|
458
|
+
int qp_get_codec_bit_rate(qp_movie_context *movie_ctx, int type)
|
459
|
+
{
|
460
|
+
AVCodecContext *decoder_ctx = NULL;
|
461
|
+
|
462
|
+
decoder_ctx = get_decoder_context(movie_ctx, type);
|
463
|
+
if (!decoder_ctx) {
|
464
|
+
return 0;
|
465
|
+
}
|
466
|
+
|
467
|
+
return decoder_ctx->bit_rate;
|
468
|
+
}
|
469
|
+
|
470
|
+
/*
|
471
|
+
static int movie_get_buffer (AVCodecContext *context, AVFrame *picture)
|
472
|
+
{
|
473
|
+
char *buf = NULL;
|
474
|
+
long bufsize = 0;
|
475
|
+
int width;
|
476
|
+
int height;
|
477
|
+
|
478
|
+
width = context->width;
|
479
|
+
height = context->height;
|
480
|
+
|
481
|
+
if (avcodec_check_dimensions(context, width, height)) {
|
482
|
+
return -1;
|
483
|
+
}
|
484
|
+
|
485
|
+
switch (context->codec_type) {
|
486
|
+
case CODEC_TYPE_VIDEO:
|
487
|
+
avcodec_align_dimensions (context, &width, &height);
|
488
|
+
|
489
|
+
bufsize = avpicture_get_size (context->pix_fmt, width, height);
|
490
|
+
|
491
|
+
if (width != context->width || height != context->height) {
|
492
|
+
context->width = width;
|
493
|
+
context->height = height;
|
494
|
+
|
495
|
+
return avcodec_default_get_buffer (context, picture);
|
496
|
+
}
|
497
|
+
|
498
|
+
buf = av_mallocz(bufsize);
|
499
|
+
//printf("get buffer %p\n", buf);
|
500
|
+
|
501
|
+
avpicture_fill((AVPicture *) picture, buf,
|
502
|
+
context->pix_fmt, context->width, context->height);
|
503
|
+
break;
|
504
|
+
case CODEC_TYPE_AUDIO:
|
505
|
+
default:
|
506
|
+
//assert("movie_get_buffer: Not implemented for audio");
|
507
|
+
break;
|
508
|
+
}
|
509
|
+
|
510
|
+
// tell ffmpeg we own this buffer
|
511
|
+
picture->type = FF_BUFFER_TYPE_USER;
|
512
|
+
|
513
|
+
return 0;
|
514
|
+
}
|
515
|
+
|
516
|
+
static void movie_release_buffer(AVCodecContext *context, AVFrame *picture)
|
517
|
+
{
|
518
|
+
int i;
|
519
|
+
char *buf = NULL;
|
520
|
+
|
521
|
+
if (picture->type != FF_BUFFER_TYPE_USER) {
|
522
|
+
return;
|
523
|
+
}
|
524
|
+
|
525
|
+
buf = (char*) (picture->opaque);
|
526
|
+
if (buf == NULL) {
|
527
|
+
return;
|
528
|
+
}
|
529
|
+
|
530
|
+
av_free(buf);
|
531
|
+
|
532
|
+
picture->opaque = NULL;
|
533
|
+
|
534
|
+
// zero out the reference in ffmpeg
|
535
|
+
for (i = 0; i < 4; i++) {
|
536
|
+
picture->data[i] = NULL;
|
537
|
+
picture->linesize[i] = 0;
|
538
|
+
}
|
539
|
+
}
|
540
|
+
*/
|
541
|
+
|
542
|
+
static void rewind_movie(qp_movie_context *movie_ctx) {
|
543
|
+
if (
|
544
|
+
|
545
|
+
#if LIBAVFORMAT_BUILD >= 4619
|
546
|
+
av_seek_frame(movie_ctx->fmt_ctx, -1, 0, 0)
|
547
|
+
#else
|
548
|
+
av_seek_frame(movie_ctx->fmt_ctx, -1, 0)
|
549
|
+
#endif
|
550
|
+
< 0) {
|
551
|
+
|
552
|
+
/* If we can't seek, fall back to reopening the file. */
|
553
|
+
qp_open_movie_file(movie_ctx, qp_get_file_name(movie_ctx));
|
554
|
+
}
|
555
|
+
|
556
|
+
movie_ctx->frame_number = 0;
|
557
|
+
}
|
558
|
+
|
559
|
+
/*
|
560
|
+
* get_av_frame()
|
561
|
+
* Returns an av_frame from the movie
|
562
|
+
*/
|
563
|
+
static AVFrame* get_av_frame(qp_movie_context *movie_ctx,
|
564
|
+
int wanted_frame, int *is_keyframe, int64_t *pts)
|
565
|
+
{
|
566
|
+
AVCodecContext *decoder_ctx = NULL;
|
567
|
+
AVPacket packet;
|
568
|
+
AVFrame *frame = NULL;
|
569
|
+
int got_frame;
|
570
|
+
int video_stream;
|
571
|
+
|
572
|
+
video_stream = get_stream_index(movie_ctx->fmt_ctx,
|
573
|
+
CODEC_TYPE_VIDEO);
|
574
|
+
if (video_stream < 0) {
|
575
|
+
return NULL;
|
576
|
+
}
|
577
|
+
|
578
|
+
decoder_ctx = get_decoder_context(movie_ctx, CODEC_TYPE_VIDEO);
|
579
|
+
if (decoder_ctx == NULL) {
|
580
|
+
return NULL;
|
581
|
+
}
|
582
|
+
|
583
|
+
/* set buffer functions */
|
584
|
+
//decoder_ctx->get_buffer = movie_get_buffer;
|
585
|
+
//decoder_ctx->release_buffer = movie_release_buffer;
|
586
|
+
|
587
|
+
/* Rewind to the beginning of the stream if wanted frame already passed */
|
588
|
+
if (wanted_frame > 0 && wanted_frame <= movie_ctx->frame_number) {
|
589
|
+
|
590
|
+
rewind_movie(movie_ctx);
|
591
|
+
|
592
|
+
/* flush decoder buffers here */
|
593
|
+
avcodec_flush_buffers(decoder_ctx);
|
594
|
+
}
|
595
|
+
|
596
|
+
frame = avcodec_alloc_frame();
|
597
|
+
|
598
|
+
/* read frames looking for wanted_frame */
|
599
|
+
while (av_read_frame(movie_ctx->fmt_ctx, &packet) >= 0) {
|
600
|
+
|
601
|
+
/* hurry up if we're still a ways from the target frame */
|
602
|
+
if (wanted_frame != QP_GETFRAME_KEYFRAME &&
|
603
|
+
wanted_frame != QP_GETFRAME_NEXTFRAME &&
|
604
|
+
wanted_frame - movie_ctx->frame_number >
|
605
|
+
decoder_ctx->gop_size + 1) {
|
606
|
+
decoder_ctx->hurry_up = 1;
|
607
|
+
} else {
|
608
|
+
decoder_ctx->hurry_up = 0;
|
609
|
+
}
|
610
|
+
|
611
|
+
if (packet.stream_index == video_stream) {
|
612
|
+
|
613
|
+
avcodec_decode_video(decoder_ctx, frame, &got_frame,
|
614
|
+
packet.data, packet.size);
|
615
|
+
|
616
|
+
if (got_frame) {
|
617
|
+
movie_ctx->frame_number++;
|
618
|
+
/* FIXME:
|
619
|
+
* With the addition of the keyframe logic, this loop is
|
620
|
+
* getting a little too tricky. wanted_frame is way
|
621
|
+
* overloaded. Refactor to make clearer what is going on.
|
622
|
+
*/
|
623
|
+
|
624
|
+
/*
|
625
|
+
* if caller wants next keyframe then get it and break out of
|
626
|
+
* loop.
|
627
|
+
*/
|
628
|
+
if (wanted_frame == QP_GETFRAME_KEYFRAME &&
|
629
|
+
(packet.flags & PKT_FLAG_KEY)) {
|
630
|
+
/* free wanted frame packet */
|
631
|
+
*is_keyframe = 1;
|
632
|
+
*pts = packet.pts;
|
633
|
+
av_free_packet(&packet);
|
634
|
+
goto found_frame;
|
635
|
+
}
|
636
|
+
|
637
|
+
if (wanted_frame == QP_GETFRAME_NEXTFRAME ||
|
638
|
+
movie_ctx->frame_number == wanted_frame) {
|
639
|
+
/* free wanted frame packet */
|
640
|
+
*is_keyframe = (packet.flags & PKT_FLAG_KEY);
|
641
|
+
*pts = packet.pts;
|
642
|
+
av_free_packet(&packet);
|
643
|
+
goto found_frame;
|
644
|
+
}
|
645
|
+
}
|
646
|
+
}
|
647
|
+
|
648
|
+
/* free the packet allocated by av_read_frame */
|
649
|
+
av_free_packet(&packet);
|
650
|
+
}
|
651
|
+
|
652
|
+
/* free av_frame struct. Buffers are managed by the codec context
|
653
|
+
and will be freed when the codec is closed */
|
654
|
+
free(frame);
|
655
|
+
return NULL;
|
656
|
+
|
657
|
+
found_frame:
|
658
|
+
return frame;
|
659
|
+
}
|
660
|
+
|
661
|
+
|
662
|
+
int qp_get_frame(qp_movie_context *movie_ctx, int wanted_frame,
|
663
|
+
qp_frame_context **frame_ctx_handle) {
|
664
|
+
int is_keyframe = 0;
|
665
|
+
int64_t pts;
|
666
|
+
AVFrame *decoded_frame = NULL;
|
667
|
+
qp_frame_context *frame_ctx = NULL;
|
668
|
+
|
669
|
+
decoded_frame = get_av_frame(movie_ctx, wanted_frame, &is_keyframe, &pts);
|
670
|
+
if (!decoded_frame) {
|
671
|
+
fprintf(stderr, "Error getting av_frame\n");
|
672
|
+
return -1;
|
673
|
+
}
|
674
|
+
|
675
|
+
/* TODO: Create higher level function in qp_frame.c for this */
|
676
|
+
if (*frame_ctx_handle == NULL) {
|
677
|
+
*frame_ctx_handle = qp_alloc_frame_ctx(NULL);
|
678
|
+
if (*frame_ctx_handle == NULL) {
|
679
|
+
fprintf(stderr, "Error allocating frame context\n");
|
680
|
+
free(decoded_frame);
|
681
|
+
return -2;
|
682
|
+
}
|
683
|
+
}
|
684
|
+
|
685
|
+
frame_ctx = *frame_ctx_handle;
|
686
|
+
|
687
|
+
/* TODO: Provide function(s) for setting these in frame.c */
|
688
|
+
frame_ctx->width = qp_get_movie_width(movie_ctx);
|
689
|
+
frame_ctx->height = qp_get_movie_height(movie_ctx);
|
690
|
+
frame_ctx->pixel_format = get_format_number(movie_ctx, CODEC_TYPE_VIDEO);
|
691
|
+
frame_ctx->keyframe = is_keyframe;
|
692
|
+
frame_ctx->pts = pts;
|
693
|
+
|
694
|
+
frame_ctx->av_frame = avcodec_alloc_frame();
|
695
|
+
avpicture_alloc((AVPicture*)frame_ctx->av_frame, frame_ctx->pixel_format,
|
696
|
+
frame_ctx->width, frame_ctx->height);
|
697
|
+
|
698
|
+
/*
|
699
|
+
* FIXME: temporary hack until I figure out how to pass new buffers
|
700
|
+
* to the decoder
|
701
|
+
*/
|
702
|
+
img_copy((AVPicture*)frame_ctx->av_frame,
|
703
|
+
(AVPicture *)decoded_frame, frame_ctx->pixel_format,
|
704
|
+
frame_ctx->width, frame_ctx->height);
|
705
|
+
|
706
|
+
free(decoded_frame);
|
707
|
+
|
708
|
+
return 0;
|
709
|
+
}
|
710
|
+
|
711
|
+
|
712
|
+
/*
|
713
|
+
* pre_read_frame()
|
714
|
+
* ffmpeg seems not to fill in some AVCodecContext fields until at least
|
715
|
+
* one frame is read. This function will read a frame without moving the
|
716
|
+
* frame counter.
|
717
|
+
*/
|
718
|
+
static void pre_read_frame(qp_movie_context *movie_ctx) {
|
719
|
+
AVFrame *frame = NULL;
|
720
|
+
int is_keyframe;
|
721
|
+
int64_t pts;
|
722
|
+
|
723
|
+
frame = get_av_frame(movie_ctx, QP_GETFRAME_NEXTFRAME, &is_keyframe, &pts);
|
724
|
+
|
725
|
+
// We only get called if frames haven't been read so we can just
|
726
|
+
// rewind once we're through
|
727
|
+
rewind_movie(movie_ctx);
|
728
|
+
if (frame) {
|
729
|
+
free(frame);
|
730
|
+
}
|
731
|
+
}
|
732
|
+
|
733
|
+
|
734
|
+
double qp_get_pixel_aspect_ratio(qp_movie_context *movie_ctx)
|
735
|
+
{
|
736
|
+
AVCodecContext *decoder_ctx = NULL;
|
737
|
+
|
738
|
+
decoder_ctx = get_decoder_context(movie_ctx, CODEC_TYPE_VIDEO);
|
739
|
+
if (!decoder_ctx) {
|
740
|
+
return 0;
|
741
|
+
}
|
742
|
+
|
743
|
+
if (decoder_ctx->sample_aspect_ratio.num == 0 &&
|
744
|
+
movie_ctx->frame_number == 0) {
|
745
|
+
// pre read a frame so ffmpeg will fill in sample aspect ratio field.
|
746
|
+
pre_read_frame(movie_ctx);
|
747
|
+
|
748
|
+
if (decoder_ctx->sample_aspect_ratio.num == 0) {
|
749
|
+
return 0;
|
750
|
+
}
|
751
|
+
}
|
752
|
+
|
753
|
+
return av_q2d(decoder_ctx->sample_aspect_ratio);
|
754
|
+
}
|
755
|
+
|
756
|
+
|
757
|
+
// TODO: Make these fields in qp_movie_context struct
|
758
|
+
//int16_t *movie_ctx->audio_samples;
|
759
|
+
int audio_input_frame_size;
|
760
|
+
|
761
|
+
|
762
|
+
int open_audio(qp_movie_context *movie_ctx, AVFormatContext *oc, AVStream *st)
|
763
|
+
{
|
764
|
+
AVCodecContext *c;
|
765
|
+
AVCodec *codec;
|
766
|
+
|
767
|
+
c = st->codec;
|
768
|
+
|
769
|
+
/* find the audio encoder */
|
770
|
+
codec = avcodec_find_encoder(c->codec_id);
|
771
|
+
if (!codec) {
|
772
|
+
fprintf(stderr, "codec not found\n");
|
773
|
+
return 1;
|
774
|
+
}
|
775
|
+
|
776
|
+
/* open it */
|
777
|
+
if (avcodec_open(c, codec) < 0) {
|
778
|
+
fprintf(stderr, "could not open codec\n");
|
779
|
+
return 2;
|
780
|
+
}
|
781
|
+
|
782
|
+
movie_ctx->audio_outbuf_size = 4 * MAX_AUDIO_PACKET_SIZE;
|
783
|
+
movie_ctx->audio_outbuf = (uint8_t *)malloc(movie_ctx->audio_outbuf_size);
|
784
|
+
|
785
|
+
/* ugly hack for PCM codecs (will be removed ASAP with new PCM
|
786
|
+
support to compute the input frame size in movie_ctx->audio_samples */
|
787
|
+
if (c->frame_size <= 1) {
|
788
|
+
audio_input_frame_size = movie_ctx->audio_outbuf_size / c->channels;
|
789
|
+
switch(st->codec->codec_id) {
|
790
|
+
case CODEC_ID_PCM_S16LE:
|
791
|
+
case CODEC_ID_PCM_S16BE:
|
792
|
+
case CODEC_ID_PCM_U16LE:
|
793
|
+
case CODEC_ID_PCM_U16BE:
|
794
|
+
audio_input_frame_size >>= 1;
|
795
|
+
break;
|
796
|
+
default:
|
797
|
+
break;
|
798
|
+
}
|
799
|
+
} else {
|
800
|
+
audio_input_frame_size = c->frame_size;
|
801
|
+
}
|
802
|
+
movie_ctx->audio_samples = (int16_t *)malloc(audio_input_frame_size * 2 * c->channels);
|
803
|
+
return 0;
|
804
|
+
}
|
805
|
+
|
806
|
+
|
807
|
+
|
808
|
+
int write_audio_frame(qp_movie_context *movie_ctx, AVFormatContext *oc,
|
809
|
+
AVStream *st)
|
810
|
+
{
|
811
|
+
AVCodecContext *c;
|
812
|
+
AVPacket pkt;
|
813
|
+
av_init_packet(&pkt);
|
814
|
+
|
815
|
+
c = st->codec;
|
816
|
+
|
817
|
+
pkt.size = avcodec_encode_audio(c, movie_ctx->audio_outbuf,
|
818
|
+
movie_ctx->audio_outbuf_size, (short *)movie_ctx->audio_samples);
|
819
|
+
|
820
|
+
pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, st->time_base);
|
821
|
+
pkt.flags |= PKT_FLAG_KEY;
|
822
|
+
pkt.stream_index= st->index;
|
823
|
+
pkt.data= movie_ctx->audio_outbuf;
|
824
|
+
|
825
|
+
/* write the compressed frame in the media file */
|
826
|
+
if (av_write_frame(oc, &pkt) != 0) {
|
827
|
+
fprintf(stderr, "Error while writing audio frame\n");
|
828
|
+
return 2;
|
829
|
+
}
|
830
|
+
return 0;
|
831
|
+
}
|
832
|
+
|
833
|
+
/*
|
834
|
+
* add an audio output stream
|
835
|
+
*/
|
836
|
+
AVStream *add_audio_stream(AVCodecContext *icodec,
|
837
|
+
AVFormatContext *oc, int codec_id)
|
838
|
+
{
|
839
|
+
AVCodecContext *c;
|
840
|
+
AVStream *st;
|
841
|
+
|
842
|
+
st = av_new_stream(oc, 1);
|
843
|
+
if (!st) {
|
844
|
+
fprintf(stderr, "Could not alloc stream\n");
|
845
|
+
return NULL;
|
846
|
+
}
|
847
|
+
|
848
|
+
c = st->codec;
|
849
|
+
c->codec_id = codec_id;
|
850
|
+
c->codec_type = CODEC_TYPE_AUDIO;
|
851
|
+
|
852
|
+
/* put sample parameters */
|
853
|
+
c->bit_rate = icodec->bit_rate;
|
854
|
+
c->sample_rate = icodec->sample_rate;
|
855
|
+
c->channels = icodec->channels;
|
856
|
+
return st;
|
857
|
+
}
|
858
|
+
|
859
|
+
|
860
|
+
/* add a video output stream */
|
861
|
+
static AVStream *add_video_stream(qp_movie_context *movie_ctx,
|
862
|
+
AVFormatContext *oc, int codec_id)
|
863
|
+
{
|
864
|
+
AVCodecContext *c;
|
865
|
+
AVStream *st;
|
866
|
+
|
867
|
+
st = av_new_stream(oc, 0);
|
868
|
+
if (!st) {
|
869
|
+
fprintf(stderr, "Could not alloc stream\n");
|
870
|
+
return NULL;
|
871
|
+
}
|
872
|
+
|
873
|
+
c = st->codec;
|
874
|
+
c->codec_id = codec_id;
|
875
|
+
c->codec_type = CODEC_TYPE_VIDEO;
|
876
|
+
|
877
|
+
/* put sample parameters */
|
878
|
+
// TODO: This should be input video stream bitrate not total bit rate
|
879
|
+
c->bit_rate = qp_get_bit_rate(movie_ctx);
|
880
|
+
/* resolution must be a multiple of two */
|
881
|
+
c->width = qp_get_movie_width(movie_ctx);
|
882
|
+
c->height = qp_get_movie_height(movie_ctx);
|
883
|
+
/* time base: this is the fundamental unit of time (in seconds) in terms
|
884
|
+
of which frame timestamps are represented. for fixed-fps content,
|
885
|
+
timebase should be 1/framerate and timestamp increments should be
|
886
|
+
identically 1. */
|
887
|
+
//c->time_base.den = STREAM_FRAME_RATE;
|
888
|
+
// TODO: Should probably just pass num/den from decoder ctx
|
889
|
+
c->time_base.den = qp_get_frame_rate(movie_ctx);
|
890
|
+
c->time_base.num = 1;
|
891
|
+
|
892
|
+
c->gop_size = 12; /* emit one intra frame every twelve frames at most */
|
893
|
+
c->pix_fmt = PIX_FMT_YUV420P;
|
894
|
+
if (c->codec_id == CODEC_ID_MPEG2VIDEO) {
|
895
|
+
/* just for testing, we also add B frames */
|
896
|
+
c->max_b_frames = 2;
|
897
|
+
}
|
898
|
+
if (c->codec_id == CODEC_ID_MPEG1VIDEO){
|
899
|
+
/* needed to avoid using macroblocks in which some coeffs overflow
|
900
|
+
this doesnt happen with normal video, it just happens here as the
|
901
|
+
motion of the chroma plane doesnt match the luma plane */
|
902
|
+
c->mb_decision=2;
|
903
|
+
}
|
904
|
+
// some formats want stream headers to be seperate
|
905
|
+
if (!strcmp(oc->oformat->name, "mp4") ||
|
906
|
+
!strcmp(oc->oformat->name, "mov") ||
|
907
|
+
!strcmp(oc->oformat->name, "3gp")) {
|
908
|
+
c->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
909
|
+
}
|
910
|
+
|
911
|
+
return st;
|
912
|
+
}
|
913
|
+
|
914
|
+
|
915
|
+
AVFrame *alloc_picture(int pix_fmt, int width, int height)
|
916
|
+
{
|
917
|
+
AVFrame *picture;
|
918
|
+
uint8_t *picture_buf;
|
919
|
+
int size;
|
920
|
+
|
921
|
+
picture = avcodec_alloc_frame();
|
922
|
+
if (!picture)
|
923
|
+
return NULL;
|
924
|
+
size = avpicture_get_size(pix_fmt, width, height);
|
925
|
+
picture_buf = (uint8_t *)malloc(size);
|
926
|
+
if (!picture_buf) {
|
927
|
+
av_free(picture);
|
928
|
+
return NULL;
|
929
|
+
}
|
930
|
+
avpicture_fill((AVPicture *)picture, picture_buf,
|
931
|
+
pix_fmt, width, height);
|
932
|
+
return picture;
|
933
|
+
}
|
934
|
+
|
935
|
+
|
936
|
+
static int write_video_frame(qp_movie_context *movie_ctx, AVFormatContext *oc,
|
937
|
+
AVStream *st, AVFrame *decoded_frame, int pixel_format)
|
938
|
+
{
|
939
|
+
int out_size, ret;
|
940
|
+
AVCodecContext *c;
|
941
|
+
|
942
|
+
c = st->codec;
|
943
|
+
|
944
|
+
// encode the image
|
945
|
+
out_size = avcodec_encode_video(c, movie_ctx->video_outbuf, movie_ctx->video_outbuf_size, decoded_frame);
|
946
|
+
// if zero size, it means the image was buffered
|
947
|
+
if (out_size > 0) {
|
948
|
+
AVPacket pkt;
|
949
|
+
av_init_packet(&pkt);
|
950
|
+
|
951
|
+
pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, st->time_base);
|
952
|
+
if (c->coded_frame->key_frame) {
|
953
|
+
pkt.flags |= PKT_FLAG_KEY;
|
954
|
+
}
|
955
|
+
pkt.stream_index= st->index;
|
956
|
+
pkt.data= movie_ctx->video_outbuf;
|
957
|
+
pkt.size= out_size;
|
958
|
+
|
959
|
+
// write the compressed frame in the media file
|
960
|
+
ret = av_write_frame(oc, &pkt);
|
961
|
+
} else {
|
962
|
+
ret = 0;
|
963
|
+
}
|
964
|
+
if (ret != 0) {
|
965
|
+
fprintf(stderr, "Error while writing video frame\n");
|
966
|
+
}
|
967
|
+
return ret;
|
968
|
+
}
|
969
|
+
|
970
|
+
|
971
|
+
static void close_audio(qp_movie_context *movie_ctx, AVFormatContext *oc,
|
972
|
+
AVStream *st)
|
973
|
+
{
|
974
|
+
avcodec_close(st->codec);
|
975
|
+
|
976
|
+
av_free(movie_ctx->audio_samples);
|
977
|
+
av_free(movie_ctx->audio_outbuf);
|
978
|
+
}
|
979
|
+
|
980
|
+
|
981
|
+
int qp_export_movie(qp_movie_context *movie_ctx, const char *filename)
|
982
|
+
{
|
983
|
+
AVFrame *decoded_frame = NULL;
|
984
|
+
AVPacket packet;
|
985
|
+
AVCodecContext *video_decoder_ctx = NULL;
|
986
|
+
AVCodecContext *audio_decoder_ctx = NULL;
|
987
|
+
AVCodec *video_codec = NULL;
|
988
|
+
//AVCodec *input_audio_codec = NULL;
|
989
|
+
AVOutputFormat *fmt = NULL;
|
990
|
+
AVFormatContext *oc = NULL;
|
991
|
+
AVStream *video_st = NULL;
|
992
|
+
AVStream *audio_st = NULL;
|
993
|
+
int video_stream_index = -1;
|
994
|
+
int audio_stream_index = -1;
|
995
|
+
int i;
|
996
|
+
int got_frame;
|
997
|
+
|
998
|
+
/* auto detect the output format from the name. default is mpeg. */
|
999
|
+
fmt = guess_format(NULL, filename, NULL);
|
1000
|
+
if (!fmt) {
|
1001
|
+
printf("Could not deduce output format from file extension: using MPEG.\n");
|
1002
|
+
fmt = guess_format("mpeg", NULL, NULL);
|
1003
|
+
}
|
1004
|
+
if (!fmt) {
|
1005
|
+
fprintf(stderr, "Could not find suitable output format\n");
|
1006
|
+
return 1;
|
1007
|
+
}
|
1008
|
+
|
1009
|
+
/* allocate the output media context */
|
1010
|
+
oc = av_alloc_format_context();
|
1011
|
+
if (!oc) {
|
1012
|
+
fprintf(stderr, "Memory error\n");
|
1013
|
+
return 2;
|
1014
|
+
}
|
1015
|
+
oc->oformat = fmt;
|
1016
|
+
snprintf(oc->filename, sizeof(oc->filename), "%s", filename);
|
1017
|
+
|
1018
|
+
// add the audio and video streams using the default format codecs
|
1019
|
+
// and initialize the codecs
|
1020
|
+
|
1021
|
+
// create a video stream if the input has a video stream and the
|
1022
|
+
// output format supports video
|
1023
|
+
if (fmt->video_codec != CODEC_ID_NONE && qp_has_video(movie_ctx)) {
|
1024
|
+
video_stream_index = get_stream_index(movie_ctx->fmt_ctx,
|
1025
|
+
CODEC_TYPE_VIDEO);
|
1026
|
+
if (video_stream_index < 0) {
|
1027
|
+
fprintf(stderr, "can't find video stream index\n");
|
1028
|
+
return 3;
|
1029
|
+
}
|
1030
|
+
|
1031
|
+
video_st = add_video_stream(movie_ctx, oc, fmt->video_codec);
|
1032
|
+
video_decoder_ctx = get_decoder_context(movie_ctx, CODEC_TYPE_VIDEO);
|
1033
|
+
}
|
1034
|
+
|
1035
|
+
// create a audio stream if the input has a audio stream and the
|
1036
|
+
// output format supports audio
|
1037
|
+
if (fmt->audio_codec != CODEC_ID_NONE && qp_has_audio(movie_ctx)) {
|
1038
|
+
audio_stream_index = get_stream_index(movie_ctx->fmt_ctx,
|
1039
|
+
CODEC_TYPE_AUDIO);
|
1040
|
+
if (audio_stream_index < 0) {
|
1041
|
+
fprintf(stderr, "can't find audio stream index\n");
|
1042
|
+
return 4;
|
1043
|
+
}
|
1044
|
+
audio_st = add_audio_stream(movie_ctx->fmt_ctx->streams[audio_stream_index]->codec, oc, fmt->audio_codec);
|
1045
|
+
audio_decoder_ctx = get_decoder_context(movie_ctx, CODEC_TYPE_AUDIO);
|
1046
|
+
}
|
1047
|
+
|
1048
|
+
/* set the output parameters (must be done even if no parameters). */
|
1049
|
+
if (av_set_parameters(oc, NULL) < 0) {
|
1050
|
+
fprintf(stderr, "Invalid output format parameters\n");
|
1051
|
+
return 5;
|
1052
|
+
}
|
1053
|
+
|
1054
|
+
//dump_format(oc, 0, filename, 1);
|
1055
|
+
|
1056
|
+
/* now that all the parameters are set, we can open the audio and
|
1057
|
+
video codecs and allocate the necessary encode buffers */
|
1058
|
+
|
1059
|
+
/* find the video encoder */
|
1060
|
+
// TODO: Might have to do the default codec thing here
|
1061
|
+
if (video_st) {
|
1062
|
+
video_codec = avcodec_find_encoder(video_st->codec->codec_id);
|
1063
|
+
if (!video_codec) {
|
1064
|
+
fprintf(stderr, "codec not found\n");
|
1065
|
+
return 6;
|
1066
|
+
}
|
1067
|
+
|
1068
|
+
/* open the codec */
|
1069
|
+
if (avcodec_open(video_st->codec, video_codec) < 0) {
|
1070
|
+
fprintf(stderr, "could not open codec\n");
|
1071
|
+
return 7;
|
1072
|
+
}
|
1073
|
+
|
1074
|
+
movie_ctx->video_outbuf = NULL;
|
1075
|
+
// TODO: need to correctly handle raw picture as in
|
1076
|
+
// output_example.c
|
1077
|
+
if (!(oc->oformat->flags & AVFMT_RAWPICTURE)) {
|
1078
|
+
/* allocate output buffer */
|
1079
|
+
/* XXX: API change will be done */
|
1080
|
+
movie_ctx->video_outbuf_size = 200000;
|
1081
|
+
movie_ctx->video_outbuf = (uint8_t *)malloc(movie_ctx->video_outbuf_size);
|
1082
|
+
}
|
1083
|
+
}
|
1084
|
+
|
1085
|
+
if (audio_st) {
|
1086
|
+
open_audio(movie_ctx, oc, audio_st);
|
1087
|
+
}
|
1088
|
+
|
1089
|
+
/* open the output file, if needed */
|
1090
|
+
if (!(fmt->flags & AVFMT_NOFILE)) {
|
1091
|
+
if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0) {
|
1092
|
+
fprintf(stderr, "Could not open '%s'\n", filename);
|
1093
|
+
return 9;
|
1094
|
+
}
|
1095
|
+
}
|
1096
|
+
|
1097
|
+
/* write the stream header, if any */
|
1098
|
+
av_write_header(oc);
|
1099
|
+
|
1100
|
+
rewind_movie(movie_ctx);
|
1101
|
+
|
1102
|
+
decoded_frame = avcodec_alloc_frame();
|
1103
|
+
|
1104
|
+
while (av_read_frame(movie_ctx->fmt_ctx, &packet) >= 0) {
|
1105
|
+
if (packet.stream_index == video_stream_index && video_st) {
|
1106
|
+
|
1107
|
+
avcodec_decode_video(video_decoder_ctx, decoded_frame,
|
1108
|
+
&got_frame, packet.data, packet.size);
|
1109
|
+
|
1110
|
+
if (got_frame) {
|
1111
|
+
|
1112
|
+
// TODO: this allocates a new frame buffer for each frame
|
1113
|
+
// conversion which is way inefficient.
|
1114
|
+
qp_convert_av_frame(&decoded_frame,
|
1115
|
+
get_format_number(movie_ctx, CODEC_TYPE_VIDEO),
|
1116
|
+
PIX_FMT_YUV420P,
|
1117
|
+
qp_get_movie_width(movie_ctx),
|
1118
|
+
qp_get_movie_height(movie_ctx), 0);
|
1119
|
+
|
1120
|
+
// TODO: Allow resizing movie during export
|
1121
|
+
//qp_resample_frame();
|
1122
|
+
|
1123
|
+
/* write interleaved audio and video frames */
|
1124
|
+
write_video_frame(movie_ctx, oc, video_st, decoded_frame,
|
1125
|
+
get_format_number(movie_ctx, CODEC_TYPE_VIDEO));
|
1126
|
+
}
|
1127
|
+
} else if (packet.stream_index == audio_stream_index && audio_st) {
|
1128
|
+
int out_size = 0;
|
1129
|
+
|
1130
|
+
movie_ctx->audio_samples =
|
1131
|
+
av_fast_realloc(movie_ctx->audio_samples,
|
1132
|
+
&movie_ctx->audio_samples_size,
|
1133
|
+
MAX(packet.size, AVCODEC_MAX_AUDIO_FRAME_SIZE));
|
1134
|
+
|
1135
|
+
avcodec_decode_audio(audio_decoder_ctx,
|
1136
|
+
movie_ctx->audio_samples, &out_size, packet.data,
|
1137
|
+
packet.size);
|
1138
|
+
|
1139
|
+
if (out_size > 0) {
|
1140
|
+
write_audio_frame(movie_ctx, oc, audio_st);
|
1141
|
+
}
|
1142
|
+
} else {
|
1143
|
+
// nop
|
1144
|
+
}
|
1145
|
+
|
1146
|
+
/* free the packet allocated by av_read_frame */
|
1147
|
+
av_free_packet(&packet);
|
1148
|
+
}
|
1149
|
+
|
1150
|
+
/* close each codec */
|
1151
|
+
if (video_st) {
|
1152
|
+
avcodec_close(video_st->codec);
|
1153
|
+
av_free(decoded_frame->data[0]);
|
1154
|
+
av_free(decoded_frame);
|
1155
|
+
av_free(movie_ctx->video_outbuf);
|
1156
|
+
}
|
1157
|
+
|
1158
|
+
if (audio_st) {
|
1159
|
+
close_audio(movie_ctx, oc, audio_st);
|
1160
|
+
}
|
1161
|
+
|
1162
|
+
/* write the trailer, if any */
|
1163
|
+
av_write_trailer(oc);
|
1164
|
+
|
1165
|
+
/* free the streams */
|
1166
|
+
for (i = 0; i < oc->nb_streams; i++) {
|
1167
|
+
av_freep(&oc->streams[i]);
|
1168
|
+
}
|
1169
|
+
|
1170
|
+
if (!(fmt->flags & AVFMT_NOFILE)) {
|
1171
|
+
/* close the output file */
|
1172
|
+
url_fclose(&oc->pb);
|
1173
|
+
}
|
1174
|
+
|
1175
|
+
/* free the stream */
|
1176
|
+
av_free(oc);
|
1177
|
+
|
1178
|
+
return 0;
|
1179
|
+
}
|