source: git/src/moviemaker-legacy.cc @ 9fcc81a

RELEASE/1.2debug-cidebug-ci-sanitiserswalls-data
Last change on this file since 9fcc81a was 420cda9, checked in by Olly Betts <olly@…>, 8 years ago

Allow seek during movie export

Some file formats require this, which meant that 1.2.27 broke export to
them.

  • Property mode set to 100644
File size: 15.8 KB
Line 
1//
2//  moviemaker.cc
3//
4//  Class for writing movies from Aven for old libav/ffmpeg
5//
6//  Copyright (C) 2004,2011,2012,2013,2014,2015,2016 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/* Based on output-example.c:
24 *
25 * Libavformat API example: Output a media file in any supported
26 * libavformat format. The default codecs are used.
27 *
28 * Copyright (c) 2003 Fabrice Bellard
29 *
30 * Permission is hereby granted, free of charge, to any person obtaining a copy
31 * of this software and associated documentation files (the "Software"), to deal
32 * in the Software without restriction, including without limitation the rights
33 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
34 * copies of the Software, and to permit persons to whom the Software is
35 * furnished to do so, subject to the following conditions:
36 *
37 * The above copyright notice and this permission notice shall be included in
38 * all copies or substantial portions of the Software.
39 *
40 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
43 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
44 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
45 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
46 * THE SOFTWARE.
47 */
48
49#ifdef HAVE_CONFIG_H
50#include <config.h>
51#endif
52
53#define __STDC_CONSTANT_MACROS
54
55#include <assert.h>
56#include <stdlib.h>
57#include <string.h>
58
59#include "moviemaker.h"
60
61#ifdef WITH_LIBAV
62extern "C" {
63# include <libavutil/imgutils.h>
64# include <libavutil/mathematics.h>
65# include <libavformat/avformat.h>
66# include <libswscale/swscale.h>
67}
68# ifndef AV_PKT_FLAG_KEY
69#  define AV_PKT_FLAG_KEY PKT_FLAG_KEY
70# endif
71# ifndef HAVE_AV_GUESS_FORMAT
72#  define av_guess_format guess_format
73# endif
74# ifndef HAVE_AVIO_OPEN
75#  define avio_open url_fopen
76# endif
77# ifndef HAVE_AVIO_CLOSE
78#  define avio_close url_fclose
79# endif
80# ifndef HAVE_AV_FRAME_ALLOC
81static inline AVFrame * av_frame_alloc() {
82    return avcodec_alloc_frame();
83}
84# endif
85# ifndef HAVE_AV_FRAME_FREE
86#  ifdef HAVE_AVCODEC_FREE_FRAME
87static inline void av_frame_free(AVFrame ** frame) {
88    avcodec_free_frame(frame);
89}
90#  else
91static inline void av_frame_free(AVFrame ** frame) {
92    free((*frame)->data[0]);
93    free(*frame);
94    *frame = NULL;
95}
96#  endif
97# endif
98# ifndef HAVE_AVCODEC_OPEN2
99// We always pass NULL for OPTS below.
100#  define avcodec_open2(CTX, CODEC, OPTS) avcodec_open(CTX, CODEC)
101# endif
102# ifndef HAVE_AVFORMAT_NEW_STREAM
103// We always pass NULL for CODEC below.
104#  define avformat_new_stream(S, CODEC) av_new_stream(S, 0)
105# endif
106# if !HAVE_DECL_AVMEDIA_TYPE_VIDEO
107#  define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO
108# endif
109# if !HAVE_DECL_AV_CODEC_ID_NONE
110#  define AV_CODEC_ID_NONE CODEC_ID_NONE
111# endif
112# if !HAVE_DECL_AV_PIX_FMT_RGB24
113#  define AV_PIX_FMT_RGB24 PIX_FMT_RGB24
114# endif
115# if !HAVE_DECL_AV_PIX_FMT_YUV420P
116#  define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
117# endif
118# ifndef AVIO_FLAG_WRITE
119#  define AVIO_FLAG_WRITE URL_WRONLY
120# endif
121
122enum {
123    MOVIE_NO_SUITABLE_FORMAT = 1,
124    MOVIE_AUDIO_ONLY,
125    MOVIE_FILENAME_TOO_LONG
126};
127
128# ifndef HAVE_AVCODEC_ENCODE_VIDEO2
129const int OUTBUF_SIZE = 200000;
130# endif
131#endif
132
133MovieMaker::MovieMaker()
134#ifdef WITH_LIBAV
135    : oc(0), video_st(0), frame(0), outbuf(0), pixels(0), sws_ctx(0), averrno(0)
136#endif
137{
138#ifdef WITH_LIBAV
139    static bool initialised_ffmpeg = false;
140    if (initialised_ffmpeg) return;
141
142    // FIXME: register only the codec(s) we want to use...
143    avcodec_register_all();
144    av_register_all();
145
146    initialised_ffmpeg = true;
147#endif
148}
149
150#ifdef WITH_LIBAV
151static int
152write_packet(void *opaque, uint8_t *buf, int buf_size) {
153    FILE * fh = (FILE*)opaque;
154    size_t res = fwrite(buf, 1, buf_size, fh);
155    return res > 0 ? res : -1;
156}
157
158static int64_t
159seek_stream(void *opaque, int64_t offset, int whence) {
160    FILE * fh = (FILE*)opaque;
161    return fseek(fh, offset, whence);
162}
163#endif
164
165#define MAX_EXTENSION_LEN 8
166
167bool MovieMaker::Open(FILE* fh, const char * ext, int width, int height)
168{
169#ifdef WITH_LIBAV
170    fh_to_close = fh;
171
172    AVOutputFormat * fmt = NULL;
173    char dummy_filename[MAX_EXTENSION_LEN + 3] = "x.";
174    if (strlen(ext) <= MAX_EXTENSION_LEN) {
175        strcpy(dummy_filename + 2, ext);
176        // Pass "x." + extension to av_guess_format() to avoid having to deal
177        // with wide character filenames.
178        fmt = av_guess_format(NULL, dummy_filename, NULL);
179    }
180    if (!fmt) {
181        // We couldn't deduce the output format from file extension so default
182        // to MPEG.
183        fmt = av_guess_format("mpeg", NULL, NULL);
184        if (!fmt) {
185            averrno = MOVIE_NO_SUITABLE_FORMAT;
186            return false;
187        }
188        strcpy(dummy_filename + 2, "mpg");
189    }
190    if (fmt->video_codec == AV_CODEC_ID_NONE) {
191        averrno = MOVIE_AUDIO_ONLY;
192        return false;
193    }
194
195    /* Allocate the output media context. */
196    oc = avformat_alloc_context();
197    if (!oc) {
198        averrno = AVERROR(ENOMEM);
199        return false;
200    }
201    oc->oformat = fmt;
202    strcpy(oc->filename, dummy_filename);
203
204    /* find the video encoder */
205    AVCodec *codec = avcodec_find_encoder(fmt->video_codec);
206    if (!codec) {
207        // FIXME : Erm - internal ffmpeg library problem?
208        averrno = AVERROR(ENOMEM);
209        return false;
210    }
211
212    // Add the video stream.
213    video_st = avformat_new_stream(oc, codec);
214    if (!video_st) {
215        averrno = AVERROR(ENOMEM);
216        return false;
217    }
218
219    // Set sample parameters.
220    AVCodecContext *c = video_st->codec;
221    c->bit_rate = 400000;
222    /* Resolution must be a multiple of two. */
223    c->width = width;
224    c->height = height;
225    /* timebase: This is the fundamental unit of time (in seconds) in terms
226     * of which frame timestamps are represented. For fixed-fps content,
227     * timebase should be 1/framerate and timestamp increments should be
228     * identical to 1. */
229#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(55, 44, 0)
230    // Old way, which now causes deprecation warnings.
231    c->time_base.den = 25; // Frames per second.
232    c->time_base.num = 1;
233#else
234    video_st->time_base.den = 25; // Frames per second.
235    video_st->time_base.num = 1;
236    c->time_base = video_st->time_base;
237#endif
238    c->gop_size = 12; /* emit one intra frame every twelve frames at most */
239    c->pix_fmt = AV_PIX_FMT_YUV420P;
240    c->rc_buffer_size = c->bit_rate * 4; // Enough for 4 seconds
241    c->rc_max_rate = c->bit_rate * 2;
242    // B frames are backwards predicted - they can improve compression,
243    // but may slow encoding and decoding.
244    // if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
245    //     c->max_b_frames = 2;
246    // }
247
248    /* Some formats want stream headers to be separate. */
249    if (oc->oformat->flags & AVFMT_GLOBALHEADER)
250        c->flags |= CODEC_FLAG_GLOBAL_HEADER;
251
252    int retval;
253#ifndef HAVE_AVFORMAT_WRITE_HEADER
254    // Set the output parameters (must be done even if no parameters).
255    retval = av_set_parameters(oc, NULL);
256    if (retval < 0) {
257        averrno = retval;
258        return false;
259    }
260#endif
261
262    retval = avcodec_open2(c, NULL, NULL);
263    if (retval < 0) {
264        averrno = retval;
265        return false;
266    }
267
268#ifndef HAVE_AVCODEC_ENCODE_VIDEO2
269    outbuf = NULL;
270    if (!(oc->oformat->flags & AVFMT_RAWPICTURE)) {
271        outbuf = (unsigned char *)av_malloc(OUTBUF_SIZE);
272        if (!outbuf) {
273            averrno = AVERROR(ENOMEM);
274            return false;
275        }
276    }
277#endif
278
279    /* Allocate the encoded raw picture. */
280    frame = av_frame_alloc();
281    if (!frame) {
282        averrno = AVERROR(ENOMEM);
283        return false;
284    }
285    retval = av_image_alloc(frame->data, frame->linesize,
286                            c->width, c->height, c->pix_fmt, 1);
287    if (retval < 0) {
288        averrno = retval;
289        return false;
290    }
291
292    if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
293        // FIXME need to allocate another frame for this case if we stop
294        // hardcoding AV_PIX_FMT_YUV420P.
295        abort();
296    }
297
298    frame->format = c->pix_fmt;
299    frame->width = c->width;
300    frame->height = c->height;
301
302    pixels = (unsigned char *)av_malloc(width * height * 6);
303    if (!pixels) {
304        averrno = AVERROR(ENOMEM);
305        return false;
306    }
307
308    // Show the format we've ended up with (for debug purposes).
309    // av_dump_format(oc, 0, fnm, 1);
310
311    av_free(sws_ctx);
312    sws_ctx = sws_getContext(width, height, AV_PIX_FMT_RGB24,
313                             width, height, c->pix_fmt, SWS_BICUBIC,
314                             NULL, NULL, NULL);
315    if (sws_ctx == NULL) {
316        fprintf(stderr, "Cannot initialize the conversion context!\n");
317        averrno = AVERROR(ENOMEM);
318        return false;
319    }
320
321    if (!(fmt->flags & AVFMT_NOFILE)) {
322        const int buf_size = 8192;
323        void * buf = av_malloc(buf_size);
324        oc->pb = avio_alloc_context(static_cast<uint8_t*>(buf), buf_size, 1,
325                                    fh, NULL, write_packet, seek_stream);
326        if (!oc->pb) {
327            averrno = AVERROR(ENOMEM);
328            return false;
329        }
330    }
331
332    // Write the stream header, if any.
333#ifdef HAVE_AVFORMAT_WRITE_HEADER
334    retval = avformat_write_header(oc, NULL);
335#else
336    retval = av_write_header(oc);
337#endif
338    if (retval < 0) {
339        averrno = retval;
340        return false;
341    }
342
343    averrno = 0;
344    return true;
345#else
346    (void)fh;
347    (void)ext;
348    (void)width;
349    (void)height;
350    return false;
351#endif
352}
353
354unsigned char * MovieMaker::GetBuffer() const {
355#ifdef WITH_LIBAV
356    return pixels + GetWidth() * GetHeight() * 3;
357#else
358    return NULL;
359#endif
360}
361
362int MovieMaker::GetWidth() const {
363#ifdef WITH_LIBAV
364    assert(video_st);
365    AVCodecContext *c = video_st->codec;
366    return c->width;
367#else
368    return 0;
369#endif
370}
371
372int MovieMaker::GetHeight() const {
373#ifdef WITH_LIBAV
374    assert(video_st);
375    AVCodecContext *c = video_st->codec;
376    return c->height;
377#else
378    return 0;
379#endif
380}
381
382bool MovieMaker::AddFrame()
383{
384#ifdef WITH_LIBAV
385    AVCodecContext * c = video_st->codec;
386
387    if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
388        // FIXME convert...
389        abort();
390    }
391
392    int len = 3 * c->width;
393    {
394        // Flip image vertically
395        int h = c->height;
396        unsigned char * src = pixels + h * len;
397        unsigned char * dest = src - len;
398        while (h--) {
399            memcpy(dest, src, len);
400            src += len;
401            dest -= len;
402        }
403    }
404    sws_scale(sws_ctx, &pixels, &len, 0, c->height, frame->data, frame->linesize);
405
406    if (oc->oformat->flags & AVFMT_RAWPICTURE) {
407        abort();
408    }
409
410    // Encode this frame.
411#ifdef HAVE_AVCODEC_ENCODE_VIDEO2
412    AVPacket pkt;
413    int got_packet;
414    av_init_packet(&pkt);
415    pkt.data = NULL;
416
417    int ret = avcodec_encode_video2(c, &pkt, frame, &got_packet);
418    if (ret < 0) {
419        averrno = ret;
420        return false;
421    }
422    if (got_packet && pkt.size) {
423        // Write the compressed frame to the media file.
424        if (pkt.pts != int64_t(AV_NOPTS_VALUE)) {
425            pkt.pts = av_rescale_q(pkt.pts,
426                                   c->time_base, video_st->time_base);
427        }
428        if (pkt.dts != int64_t(AV_NOPTS_VALUE)) {
429            pkt.dts = av_rescale_q(pkt.dts,
430                                   c->time_base, video_st->time_base);
431        }
432        pkt.stream_index = video_st->index;
433
434        /* Write the compressed frame to the media file. */
435        ret = av_interleaved_write_frame(oc, &pkt);
436        if (ret < 0) {
437            averrno = ret;
438            return false;
439        }
440    }
441#else
442    out_size = avcodec_encode_video(c, outbuf, OUTBUF_SIZE, frame);
443    // outsize == 0 means that this frame has been buffered, so there's nothing
444    // to write yet.
445    if (out_size) {
446        // Write the compressed frame to the media file.
447        AVPacket pkt;
448        av_init_packet(&pkt);
449
450        if (c->coded_frame->pts != (int64_t)AV_NOPTS_VALUE)
451            pkt.pts = av_rescale_q(c->coded_frame->pts, c->time_base, video_st->time_base);
452        if (c->coded_frame->key_frame)
453            pkt.flags |= AV_PKT_FLAG_KEY;
454        pkt.stream_index = video_st->index;
455        pkt.data = outbuf;
456        pkt.size = out_size;
457
458        /* Write the compressed frame to the media file. */
459        int ret = av_interleaved_write_frame(oc, &pkt);
460        if (ret < 0) {
461            averrno = ret;
462            return false;
463        }
464    }
465#endif
466#endif
467    return true;
468}
469
470bool
471MovieMaker::Close()
472{
473#ifdef WITH_LIBAV
474    if (video_st && averrno == 0) {
475        // No more frames to compress.  The codec may have a few frames
476        // buffered if we're using B frames, so write those too.
477        AVCodecContext * c = video_st->codec;
478
479#ifdef HAVE_AVCODEC_ENCODE_VIDEO2
480        while (1) {
481            AVPacket pkt;
482            int got_packet;
483            av_init_packet(&pkt);
484            pkt.data = NULL;
485            pkt.size = 0;
486
487            int ret = avcodec_encode_video2(c, &pkt, NULL, &got_packet);
488            if (ret < 0) {
489                release();
490                averrno = ret;
491                return false;
492            }
493            if (!got_packet) break;
494            if (!pkt.size) continue;
495
496            // Write the compressed frame to the media file.
497            if (pkt.pts != int64_t(AV_NOPTS_VALUE)) {
498                pkt.pts = av_rescale_q(pkt.pts,
499                                       c->time_base, video_st->time_base);
500            }
501            if (pkt.dts != int64_t(AV_NOPTS_VALUE)) {
502                pkt.dts = av_rescale_q(pkt.dts,
503                                       c->time_base, video_st->time_base);
504            }
505            pkt.stream_index = video_st->index;
506
507            /* Write the compressed frame to the media file. */
508            ret = av_interleaved_write_frame(oc, &pkt);
509            if (ret < 0) {
510                release();
511                averrno = ret;
512                return false;
513            }
514        }
515#else
516        while (out_size) {
517            out_size = avcodec_encode_video(c, outbuf, OUTBUF_SIZE, NULL);
518            if (out_size) {
519                // Write the compressed frame to the media file.
520                AVPacket pkt;
521                av_init_packet(&pkt);
522
523                if (c->coded_frame->pts != (int64_t)AV_NOPTS_VALUE)
524                    pkt.pts = av_rescale_q(c->coded_frame->pts, c->time_base, video_st->time_base);
525                if (c->coded_frame->key_frame)
526                    pkt.flags |= AV_PKT_FLAG_KEY;
527                pkt.stream_index = video_st->index;
528                pkt.data = outbuf;
529                pkt.size = out_size;
530
531                /* write the compressed frame in the media file */
532                int ret = av_interleaved_write_frame(oc, &pkt);
533                if (ret < 0) {
534                    release();
535                    averrno = ret;
536                    return false;
537                }
538            }
539        }
540#endif
541
542        av_write_trailer(oc);
543    }
544
545    release();
546#endif
547    return true;
548}
549
550#ifdef WITH_LIBAV
551void
552MovieMaker::release()
553{
554    if (video_st) {
555        // Close codec.
556        avcodec_close(video_st->codec);
557        video_st = NULL;
558    }
559
560    if (frame) {
561        av_frame_free(&frame);
562    }
563    av_free(pixels);
564    pixels = NULL;
565    av_free(outbuf);
566    outbuf = NULL;
567    av_free(sws_ctx);
568    sws_ctx = NULL;
569
570    if (oc) {
571        // Free the streams.
572        for (size_t i = 0; i < oc->nb_streams; ++i) {
573            av_freep(&oc->streams[i]->codec);
574            av_freep(&oc->streams[i]);
575        }
576
577        if (!(oc->oformat->flags & AVFMT_NOFILE)) {
578            // Release the AVIOContext.
579            av_free(oc->pb);
580        }
581
582        // Free the stream.
583        av_free(oc);
584        oc = NULL;
585    }
586    if (fh_to_close) {
587        fclose(fh_to_close);
588        fh_to_close = NULL;
589    }
590}
591#endif
592
593MovieMaker::~MovieMaker()
594{
595#ifdef WITH_LIBAV
596    release();
597#endif
598}
599
600const char *
601MovieMaker::get_error_string() const
602{
603#ifdef WITH_LIBAV
604    switch (averrno) {
605        case AVERROR(EIO):
606            return "I/O error";
607        case AVERROR(EDOM):
608            return "Number syntax expected in filename";
609        case AVERROR_INVALIDDATA:
610            /* same as AVERROR_UNKNOWN: return "unknown error"; */
611            return "invalid data found";
612        case AVERROR(ENOMEM):
613            return "not enough memory";
614        case AVERROR(EILSEQ):
615            return "unknown format";
616        case AVERROR(ENOSYS):
617            return "Operation not supported";
618        case AVERROR(ENOENT):
619            return "No such file or directory";
620        case AVERROR_EOF:
621            return "End of file";
622        case AVERROR_PATCHWELCOME:
623            return "Not implemented in FFmpeg";
624        case 0:
625            return "No error";
626        case MOVIE_NO_SUITABLE_FORMAT:
627            return "Couldn't find a suitable output format";
628        case MOVIE_AUDIO_ONLY:
629            return "Audio-only format specified";
630        case MOVIE_FILENAME_TOO_LONG:
631            return "Filename too long";
632    }
633#endif
634    return "Unknown error";
635}
Note: See TracBrowser for help on using the repository browser.