source: libpipi/trunk/pipi/sequence.c @ 3627

Last change on this file since 3627 was 3627, checked in by Sam Hocevar, 11 years ago

Move the whole makemovie logic to libpipi. It's really hackish
for now, but it seems to work.

File size: 7.0 KB
Line 
1/*
2 *  libpipi       Pathetic image processing interface library
3 *  Copyright (c) 2004-2009 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  $Id$
7 *
8 *  This library is free software. It comes without any warranty, to
9 *  the extent permitted by applicable law. You can redistribute it
10 *  and/or modify it under the terms of the Do What The Fuck You Want
11 *  To Public License, Version 2, as published by Sam Hocevar. See
12 *  http://sam.zoy.org/wtfpl/COPYING for more details.
13 */
14
15/*
16 * codec.c: image I/O functions
17 */
18
19#include "config.h"
20
21#if defined _WIN32
22#   undef _CRT_SECURE_NO_WARNINGS
23#   define _CRT_SECURE_NO_WARNINGS /* I know how to use snprintf, thank you */
24#   define snprintf _snprintf
25#endif
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#if defined USE_FFMPEG
32#   include <libavformat/avformat.h>
33#   include <libswscale/swscale.h>
34#endif
35
36#include "pipi.h"
37#include "pipi_internals.h"
38
39#if defined USE_FFMPEG
40typedef struct
41{
42    uint8_t *buf;
43    size_t buf_len;
44
45    AVFormatContext *fmt_ctx;
46    AVStream *stream;
47    AVCodecContext *cod_ctx;
48    AVCodec *codec;
49    AVFrame *frame;
50
51    struct SwsContext *sws_ctx;
52    int src_width, src_height;
53}
54ffmpeg_codec_t;
55#endif
56
57#define PAR_NUM 1
58#define PAR_DEN 1
59#define BITRATE (16 * 1024 * 1024)
60
61pipi_sequence_t *pipi_open_sequence(char *file, int width, int height, int fps)
62{
63#if defined USE_FFMPEG
64    static int initialised = 0;
65
66    pipi_sequence_t *seq;
67    ffmpeg_codec_t *ff;
68    uint8_t *tmp;
69
70    seq = malloc(sizeof(pipi_sequence_t));
71    seq->w = width;
72    seq->h = height;
73    seq->fps = fps;
74
75    ff = malloc(sizeof(ffmpeg_codec_t));
76    memset(ff, 0, sizeof(*ff));
77
78    seq->codec_priv = ff;
79
80    if (!initialised)
81    {
82        av_register_all();
83        initialised = 1;
84    }
85
86    ff->fmt_ctx = avformat_alloc_context();
87    if (!ff->fmt_ctx)
88        goto error;
89
90    /* Careful here: the Win32 snprintf doesn't seem to add a trailing
91     * zero to the truncated output. */
92    snprintf(ff->fmt_ctx->filename, sizeof(ff->fmt_ctx->filename),
93             file);
94    ff->fmt_ctx->filename[sizeof(ff->fmt_ctx->filename) - 1] = '\0';
95
96    ff->fmt_ctx->oformat = guess_format(NULL, file, NULL);
97    if (!ff->fmt_ctx->oformat)
98        ff->fmt_ctx->oformat = guess_format("mpeg", NULL, NULL);
99    if (!ff->fmt_ctx->oformat)
100        goto error;
101
102    ff->stream = av_new_stream(ff->fmt_ctx, 0);
103    if (!ff->stream)
104        goto error;
105
106    ff->stream->sample_aspect_ratio.num = PAR_NUM;
107    ff->stream->sample_aspect_ratio.den = PAR_DEN;
108
109    ff->cod_ctx = ff->stream->codec;
110
111    ff->cod_ctx->width = width;
112    ff->cod_ctx->height = height;
113    ff->cod_ctx->sample_aspect_ratio.num = PAR_NUM;
114    ff->cod_ctx->sample_aspect_ratio.den = PAR_DEN;
115    ff->cod_ctx->codec_id = ff->fmt_ctx->oformat->video_codec;
116    ff->cod_ctx->codec_type = CODEC_TYPE_VIDEO;
117    ff->cod_ctx->bit_rate = BITRATE;
118    ff->cod_ctx->time_base.num = 1;
119    ff->cod_ctx->time_base.den = fps;
120    ff->cod_ctx->gop_size = fps * 3 / 4; /* empirical */
121    ff->cod_ctx->pix_fmt = PIX_FMT_YUV420P; /* send YUV 420 */
122    if (ff->cod_ctx->codec_id == CODEC_ID_MPEG2VIDEO)
123        ff->cod_ctx->max_b_frames = 2;
124    if (ff->cod_ctx->codec_id == CODEC_ID_MPEG1VIDEO)
125        ff->cod_ctx->mb_decision = 2;
126    if (ff->fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
127        ff->cod_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER;
128
129    if (av_set_parameters(ff->fmt_ctx, NULL) < 0)
130        goto error;
131
132    ff->codec = avcodec_find_encoder(ff->cod_ctx->codec_id);
133    if (!ff->codec)
134        goto error;
135    if (avcodec_open(ff->cod_ctx, ff->codec) < 0)
136        goto error;
137
138    ff->frame = avcodec_alloc_frame();
139    if (!ff->frame)
140        goto error;
141    tmp = (uint8_t *)av_malloc(avpicture_get_size(ff->cod_ctx->pix_fmt,
142                                                  ff->cod_ctx->width,
143                                                  ff->cod_ctx->height));
144    if (!tmp)
145        goto error;
146    avpicture_fill((AVPicture *)ff->frame, tmp, ff->cod_ctx->pix_fmt,
147                   ff->cod_ctx->width, ff->cod_ctx->height);
148
149    if (!(ff->fmt_ctx->flags & AVFMT_NOFILE))
150        if (url_fopen(&ff->fmt_ctx->pb, file, URL_WRONLY) < 0)
151            goto error;
152
153    ff->buf_len = 64 * 1024 * 1024;
154    ff->buf = (uint8_t *)av_malloc(ff->buf_len);
155
156    av_write_header(ff->fmt_ctx);
157
158    return seq;
159
160error:
161    pipi_close_sequence(seq);
162    return NULL;
163
164#else
165    return NULL;
166
167#endif
168}
169
170int pipi_feed_sequence(pipi_sequence_t *seq, uint8_t *buffer, int width, int height)
171{
172#if defined USE_FFMPEG
173    AVPacket packet;
174    size_t bytes;
175    int pitch;
176
177    ffmpeg_codec_t *ff = (ffmpeg_codec_t *)seq->codec_priv;
178
179    if (ff->src_width != width || ff->src_height != height)
180    {
181        ff->src_width = width;
182        ff->src_height = height;
183        if (ff->sws_ctx)
184            sws_freeContext(ff->sws_ctx);
185        ff->sws_ctx = NULL;
186    }
187
188    if (!ff->sws_ctx)
189        ff->sws_ctx = sws_getContext(width, height, PIX_FMT_RGB32,
190                                       ff->cod_ctx->width,
191                                       ff->cod_ctx->height,
192                                       ff->cod_ctx->pix_fmt, SWS_BICUBIC,
193                                       NULL, NULL, NULL);
194    if (!ff->sws_ctx)
195        return -1;
196
197    pitch = width * 4;
198    sws_scale(ff->sws_ctx, &buffer, &pitch, 0, height,
199              ff->frame->data, ff->frame->linesize);
200
201    bytes = avcodec_encode_video(ff->cod_ctx, ff->buf,
202                                 ff->buf_len, ff->frame);
203    if (bytes <= 0)
204        return 0;
205
206    av_init_packet(&packet);
207    if (ff->cod_ctx->coded_frame->pts != 0x8000000000000000LL)
208        packet.pts = av_rescale_q(ff->cod_ctx->coded_frame->pts,
209                                  ff->cod_ctx->time_base, ff->stream->time_base);
210    if (ff->cod_ctx->coded_frame->key_frame)
211        packet.flags |= PKT_FLAG_KEY;
212    packet.stream_index = 0;
213    packet.data = ff->buf;
214    packet.size = bytes;
215
216    if (av_interleaved_write_frame(ff->fmt_ctx, &packet) < 0)
217        return -1;
218#endif
219
220    return 0;
221}
222
223int pipi_close_sequence(pipi_sequence_t *seq)
224{
225#if defined USE_FFMPEG
226    ffmpeg_codec_t *ff = (ffmpeg_codec_t *)seq->codec_priv;
227
228    if (ff->fmt_ctx)
229    {
230        av_write_trailer(ff->fmt_ctx);
231    }
232
233    if (ff->buf)
234    {
235        av_free(ff->buf);
236        ff->buf = NULL;
237    }
238
239    if (ff->cod_ctx)
240    {
241        avcodec_close(ff->cod_ctx);
242        ff->cod_ctx = NULL;
243    }
244
245    if (ff->frame)
246    {
247        av_free(ff->frame->data[0]);
248        av_free(ff->frame);
249        ff->frame = NULL;
250    }
251
252    if (ff->fmt_ctx)
253    {
254        av_freep(&ff->fmt_ctx->streams[0]->codec);
255        ff->codec = NULL;
256
257        av_freep(&ff->fmt_ctx->streams[0]);
258        ff->stream = NULL;
259
260        if (!(ff->fmt_ctx->flags & AVFMT_NOFILE))
261            url_fclose(ff->fmt_ctx->pb);
262
263        av_free(ff->fmt_ctx);
264        ff->fmt_ctx = NULL;
265    }
266
267    if (ff->sws_ctx)
268    {
269        sws_freeContext(ff->sws_ctx);
270        ff->sws_ctx = NULL;
271        ff->src_width = ff->src_height = 0;
272    }
273
274    free(ff);
275    free(seq);
276#endif
277
278    return 0;
279}
Note: See TracBrowser for help on using the repository browser.