source: git/src/moviemaker.cc @ ecbc6c18

RELEASE/1.1RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-data
Last change on this file since ecbc6c18 was ecbc6c18, checked in by Olly Betts <olly@…>, 14 years ago

src/: Update FSF address in licence notices.

git-svn-id: file:///home/survex-svn/survex/branches/survex-1_1@3417 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

  • Property mode set to 100644
File size: 7.1 KB
Line 
1//
2//  moviemaker.cc
3//
4//  Class for writing movies from Aven.
5//
6//  Copyright (C) 2004 Olly Betts
7//
8//  This program is free software; you can redistribute it and/or modify
9//  it under the terms of the GNU General Public License as published by
10//  the Free Software Foundation; either version 2 of the License, or
11//  (at your option) any later version.
12//
13//  This program is distributed in the hope that it will be useful,
14//  but WITHOUT ANY WARRANTY; without even the implied warranty of
15//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16//  GNU General Public License for more details.
17//
18//  You should have received a copy of the GNU General Public License
19//  along with this program; if not, write to the Free Software
20//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21//
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <assert.h>
28#include <stdlib.h>
29#include <string.h>
30
31#include "moviemaker.h"
32
33#ifdef HAVE_AVFORMAT_H
34#include "avformat.h"
35#endif
36
37// ffmpeg CVS has added av_alloc_format_context() - we're ready for it!
38#define av_alloc_format_context() \
39    ((AVFormatContext*)av_mallocz(sizeof(AVFormatContext)))
40
41const int OUTBUF_SIZE = 200000;
42
43MovieMaker::MovieMaker()
44    : oc(0), st(0), frame(0), outbuf(0), in(0), out(0), pixels(0)
45{
46#ifdef HAVE_AVFORMAT_H
47    static bool initialised_ffmpeg = false;
48    if (initialised_ffmpeg) return;
49
50    // FIXME: register only the codec(s) we want to use...
51    av_register_all();
52
53    initialised_ffmpeg = true;
54#endif
55}
56
57bool MovieMaker::Open(const char *fnm, int width, int height)
58{
59#ifdef HAVE_AVFORMAT_H
60    AVOutputFormat * fmt = guess_format(NULL, fnm, NULL);
61    if (!fmt) {
62        // We couldn't deduce the output format from file extension so default
63        // to MPEG.
64        fmt = guess_format("mpeg", NULL, NULL);
65        if (!fmt) {
66            // FIXME : error finding a format...
67            return false;
68        }
69    }
70    if (fmt->video_codec == CODEC_ID_NONE) {
71        // FIXME : The user asked for a format which is audio-only!
72        return false;
73    }
74
75    // Allocate the output media context.
76    oc = av_alloc_format_context();
77    if (!oc) {
78        // FIXME : out of memory
79        return false;
80    }
81    oc->oformat = fmt;
82    if (strlen(fnm) >= sizeof(oc->filename)) {
83        // FIXME : filename too long
84        return false;
85    }
86    strcpy(oc->filename, fnm);
87
88    // Add the video stream using the default format codec.
89    st = av_new_stream(oc, 0);
90    if (!st) {
91        // FIXME : possible errors are "too many streams" (can't be - we only
92        // ask for one) and "out of memory"
93        return false;
94    }
95
96    // Initialise the code.
97    AVCodecContext *c = &st->codec;
98    c->codec_id = fmt->video_codec;
99    c->codec_type = CODEC_TYPE_VIDEO;
100
101    // Set sample parameters.
102    c->bit_rate = 400000;
103    c->width = width;
104    c->height = height;
105    c->frame_rate = 25; // FPS
106    c->frame_rate_base = 1;
107    c->gop_size = 12; // One intra frame every twelve frames.
108    c->pix_fmt = PIX_FMT_YUV420P;
109    // B frames are backwards predicted - they can improve compression,
110    // but may slow encoding and decoding.
111    // c->max_b_frames = 2;
112
113    // Set the output parameters (must be done even if no parameters).
114    if (av_set_parameters(oc, NULL) < 0) {
115        // FIXME : return value is an AVERROR_* value - probably
116        // AVERROR_NOMEM or AVERROR_UNKNOWN.
117        return false;
118    }
119
120    // Show the format we've ended up with (for debug purposes).
121    // dump_format(oc, 0, fnm, 1);
122
123    // Open the video codec and allocate the necessary encode buffers.
124    AVCodec * codec = avcodec_find_encoder(c->codec_id);
125    if (!codec) {
126        // FIXME : Erm - internal ffmpeg library problem?
127        return false;
128    }
129    if (avcodec_open(c, codec) < 0) {
130        // FIXME : return value is an AVERROR_* value - probably
131        // AVERROR_NOMEM or AVERROR_UNKNOWN.
132        return false;
133    }
134
135    outbuf = (unsigned char *)malloc(OUTBUF_SIZE);
136    if (!outbuf) {
137        // FIXME : out of memory
138        return false;
139    }
140
141    frame = avcodec_alloc_frame();
142    in = (AVPicture *)malloc(sizeof(AVPicture));
143    out = (AVPicture *)malloc(sizeof(AVPicture));
144    if (!frame || !in || !out) {
145        // FIXME : out of memory
146        return false;
147    }
148
149    int size = width * height;
150    out->data[0] = (unsigned char *)malloc(size * 3 / 2);
151    if (!out->data[0]) {
152        // FIXME : out of memory
153        return false;
154    }
155    avpicture_fill((AVPicture *)frame, out->data[0], c->pix_fmt, width, height);
156    out->data[1] = frame->data[1];
157    out->data[2] = frame->data[2];
158    out->linesize[0] = frame->linesize[0];
159    out->linesize[1] = frame->linesize[1];
160    out->linesize[2] = frame->linesize[2];
161
162    pixels = (unsigned char *)malloc(width * height * 6);
163    if (!pixels) {
164        // FIXME : out of memory
165        return false;
166    }
167    in->data[0] = pixels + height * width * 3;
168    in->linesize[0] = width * 3;
169
170    out_size = 0;
171
172    if (url_fopen(&oc->pb, fnm, URL_WRONLY) < 0) {
173        // FIXME : return value is -E* (e.g. -EIO).
174        return false;
175    }
176
177    // Write the stream header, if any.
178    if (av_write_header(oc) < 0) {
179        // FIXME : return value is an AVERROR_* value.
180        return false;
181    }
182    return true;
183#else
184    return false;
185#endif
186}
187
188unsigned char * MovieMaker::GetBuffer() const {
189    return pixels;
190}
191
192int MovieMaker::GetWidth() const {
193    assert(st);
194#ifdef HAVE_AVFORMAT_H
195    AVCodecContext *c = &st->codec;
196    return c->width;
197#else
198    return 0;
199#endif
200}
201
202int MovieMaker::GetHeight() const {
203    assert(st);
204#ifdef HAVE_AVFORMAT_H
205    AVCodecContext *c = &st->codec;
206    return c->height;
207#else
208    return 0;
209#endif
210}
211
212void MovieMaker::AddFrame()
213{
214#ifdef HAVE_AVFORMAT_H
215    AVCodecContext * c = &st->codec;
216
217    const int len = 3 * c->width;
218    const int h = c->height;
219    // Flip image vertically
220    for (int y = 0; y < h; ++y) {
221        memcpy(pixels + (2 * h - y - 1) * len, pixels + y * len, len);
222    }
223
224    img_convert(out, PIX_FMT_YUV420P, in, PIX_FMT_RGB24, c->width, c->height);
225
226    // Encode this frame.
227    out_size = avcodec_encode_video(c, outbuf, OUTBUF_SIZE, frame);
228    // outsize == 0 means that this frame has been buffered, so there's nothing
229    // to write yet.
230    if (out_size != 0) {
231        // Write the compressed frame to the media file.
232        if (av_write_frame(oc, st->index, outbuf, out_size) != 0) {
233            fprintf(stderr, "Error while writing video frame\n");
234            exit(1);
235        }
236    }
237#endif
238}
239
240MovieMaker::~MovieMaker()
241{
242#ifdef HAVE_AVFORMAT_H
243    if (st) {
244        // No more frames to compress.  The codec may have a few frames
245        // buffered if we're using B frames, so write those too.
246        AVCodecContext * c = &st->codec;
247
248        while (out_size) {
249            out_size = avcodec_encode_video(c, outbuf, OUTBUF_SIZE, NULL);
250            if (out_size) {
251                if (av_write_frame(oc, st->index, outbuf, out_size) != 0) {
252                    fprintf(stderr, "Error while writing video frame\n");
253                    exit(1);
254                }
255            }
256        }
257
258        // Close codec.
259        avcodec_close(c);
260    }
261
262    free(frame);
263    if (out) free(out->data[0]);
264    free(outbuf);
265    free(pixels);
266    free(in);
267    free(out);
268
269    if (oc) {
270        // Write the trailer, if any.
271        av_write_trailer(oc);
272
273        // Free the streams.
274        for(int i = 0; i < oc->nb_streams; ++i) {
275            av_freep(&oc->streams[i]);
276        }
277
278        // Close the output file.
279        url_fclose(&oc->pb);
280
281        // Free the stream.
282        free(oc);
283    }
284#endif
285}
Note: See TracBrowser for help on using the repository browser.