Refactor handling of relocatable installs
[survex.git] / src / moviemaker-legacy.cc
blob9102a3a5d6b0c97df81c3b7a2e5caf8700b5b781
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.
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.
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
23 /* Based on output-example.c:
25 * Libavformat API example: Output a media file in any supported
26 * libavformat format. The default codecs are used.
28 * Copyright (c) 2003 Fabrice Bellard
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:
37 * The above copyright notice and this permission notice shall be included in
38 * all copies or substantial portions of the Software.
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.
49 #ifdef HAVE_CONFIG_H
50 #include <config.h>
51 #endif
53 #define __STDC_CONSTANT_MACROS
55 #include <assert.h>
56 #include <stdlib.h>
57 #include <string.h>
59 #include "moviemaker.h"
61 #ifdef WITH_LIBAV
62 extern "C" {
63 # include <libavutil/imgutils.h>
64 # include <libavutil/mathematics.h>
65 # include <libavformat/avformat.h>
66 # include <libswscale/swscale.h>
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
81 static inline AVFrame * av_frame_alloc() {
82 return avcodec_alloc_frame();
84 # endif
85 # ifndef HAVE_AV_FRAME_FREE
86 # ifdef HAVE_AVCODEC_FREE_FRAME
87 static inline void av_frame_free(AVFrame ** frame) {
88 avcodec_free_frame(frame);
90 # else
91 static inline void av_frame_free(AVFrame ** frame) {
92 free((*frame)->data[0]);
93 free(*frame);
94 *frame = NULL;
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
122 enum {
123 MOVIE_NO_SUITABLE_FORMAT = 1,
124 MOVIE_AUDIO_ONLY,
125 MOVIE_FILENAME_TOO_LONG
128 # ifndef HAVE_AVCODEC_ENCODE_VIDEO2
129 const int OUTBUF_SIZE = 200000;
130 # endif
131 #endif
133 MovieMaker::MovieMaker()
134 #ifdef WITH_LIBAV
135 : oc(0), video_st(0), frame(0), outbuf(0), pixels(0), sws_ctx(0), averrno(0)
136 #endif
138 #ifdef WITH_LIBAV
139 static bool initialised_ffmpeg = false;
140 if (initialised_ffmpeg) return;
142 // FIXME: register only the codec(s) we want to use...
143 avcodec_register_all();
144 av_register_all();
146 initialised_ffmpeg = true;
147 #endif
150 #ifdef WITH_LIBAV
151 static int
152 write_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;
158 static int64_t
159 seek_stream(void *opaque, int64_t offset, int whence) {
160 FILE * fh = (FILE*)opaque;
161 return fseek(fh, offset, whence);
163 #endif
165 #define MAX_EXTENSION_LEN 8
167 bool MovieMaker::Open(FILE* fh, const char * ext, int width, int height)
169 #ifdef WITH_LIBAV
170 fh_to_close = fh;
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);
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;
188 strcpy(dummy_filename + 2, "mpg");
190 if (fmt->video_codec == AV_CODEC_ID_NONE) {
191 averrno = MOVIE_AUDIO_ONLY;
192 return false;
195 /* Allocate the output media context. */
196 oc = avformat_alloc_context();
197 if (!oc) {
198 averrno = AVERROR(ENOMEM);
199 return false;
201 oc->oformat = fmt;
202 strcpy(oc->filename, dummy_filename);
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;
212 // Add the video stream.
213 video_st = avformat_new_stream(oc, codec);
214 if (!video_st) {
215 averrno = AVERROR(ENOMEM);
216 return false;
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 // }
248 /* Some formats want stream headers to be separate. */
249 if (oc->oformat->flags & AVFMT_GLOBALHEADER)
250 c->flags |= CODEC_FLAG_GLOBAL_HEADER;
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;
260 #endif
262 retval = avcodec_open2(c, NULL, NULL);
263 if (retval < 0) {
264 averrno = retval;
265 return false;
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;
277 #endif
279 /* Allocate the encoded raw picture. */
280 frame = av_frame_alloc();
281 if (!frame) {
282 averrno = AVERROR(ENOMEM);
283 return false;
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;
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();
298 frame->format = c->pix_fmt;
299 frame->width = c->width;
300 frame->height = c->height;
302 pixels = (unsigned char *)av_malloc(width * height * 6);
303 if (!pixels) {
304 averrno = AVERROR(ENOMEM);
305 return false;
308 // Show the format we've ended up with (for debug purposes).
309 // av_dump_format(oc, 0, fnm, 1);
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;
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;
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;
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
354 unsigned char * MovieMaker::GetBuffer() const {
355 #ifdef WITH_LIBAV
356 return pixels + GetWidth() * GetHeight() * 3;
357 #else
358 return NULL;
359 #endif
362 int 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
372 int 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
382 bool MovieMaker::AddFrame()
384 #ifdef WITH_LIBAV
385 AVCodecContext * c = video_st->codec;
387 if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
388 // FIXME convert...
389 abort();
392 int len = 3 * c->width;
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;
404 sws_scale(sws_ctx, &pixels, &len, 0, c->height, frame->data, frame->linesize);
406 if (oc->oformat->flags & AVFMT_RAWPICTURE) {
407 abort();
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;
417 int ret = avcodec_encode_video2(c, &pkt, frame, &got_packet);
418 if (ret < 0) {
419 averrno = ret;
420 return false;
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);
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);
432 pkt.stream_index = video_st->index;
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;
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);
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;
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;
465 #endif
466 #endif
467 return true;
470 bool
471 MovieMaker::Close()
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;
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;
487 int ret = avcodec_encode_video2(c, &pkt, NULL, &got_packet);
488 if (ret < 0) {
489 release();
490 averrno = ret;
491 return false;
493 if (!got_packet) break;
494 if (!pkt.size) continue;
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);
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);
505 pkt.stream_index = video_st->index;
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;
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);
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;
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;
540 #endif
542 av_write_trailer(oc);
545 release();
546 #endif
547 return true;
550 #ifdef WITH_LIBAV
551 void
552 MovieMaker::release()
554 if (video_st) {
555 // Close codec.
556 avcodec_close(video_st->codec);
557 video_st = NULL;
560 if (frame) {
561 av_frame_free(&frame);
563 av_free(pixels);
564 pixels = NULL;
565 av_free(outbuf);
566 outbuf = NULL;
567 av_free(sws_ctx);
568 sws_ctx = NULL;
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]);
577 if (!(oc->oformat->flags & AVFMT_NOFILE)) {
578 // Release the AVIOContext.
579 av_free(oc->pb);
582 // Free the stream.
583 av_free(oc);
584 oc = NULL;
586 if (fh_to_close) {
587 fclose(fh_to_close);
588 fh_to_close = NULL;
591 #endif
593 MovieMaker::~MovieMaker()
595 #ifdef WITH_LIBAV
596 release();
597 #endif
600 const char *
601 MovieMaker::get_error_string() const
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";
633 #endif
634 return "Unknown error";