source: git/src/moviemaker.cc @ 2c3fc2e

RELEASE/1.1RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-datawalls-data-hanging-as-warning
Last change on this file since 2c3fc2e was 1805274, checked in by Olly Betts <olly@…>, 20 years ago

Write video stream in a proper format, rather than just the raw MPEG stream.

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

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