From 23901ebc2471f0f083706c2e555d8d36a29c4e5a Mon Sep 17 00:00:00 2001 From: sam Date: Wed, 5 Aug 2009 14:03:37 +0000 Subject: [PATCH] Move the whole makemovie logic to libpipi. It's really hackish for now, but it seems to work. git-svn-id: file:///srv/caca.zoy.org/var/lib/svn/libpipi/trunk@3627 92316355-f0b4-4df1-b90c-862c8a59935f --- examples/makemovie.c | 148 ++------------------ examples/makemovie.vcproj | 192 ++++++++++++++++++++++++++ examples/sharpen.vcproj | 186 +++++++++++++++++++++++++ pipi/Makefile.am | 1 + pipi/libpipi.vcproj | 24 ++-- pipi/pipi.h | 9 +- pipi/pipi_internals.h | 9 +- pipi/sequence.c | 279 ++++++++++++++++++++++++++++++++++++++ win32/config.h | 1 + 9 files changed, 702 insertions(+), 147 deletions(-) create mode 100644 examples/makemovie.vcproj create mode 100644 examples/sharpen.vcproj create mode 100644 pipi/sequence.c diff --git a/examples/makemovie.c b/examples/makemovie.c index b5f2a24..e748ad8 100644 --- a/examples/makemovie.c +++ b/examples/makemovie.c @@ -1,6 +1,6 @@ /* * makemovie read image names from stdin and create a movie - * Copyright (c) 2009 Sam Hocevar + * Copyright (c) 2009 Sam Hocevar * All Rights Reserved * * $Id$ @@ -18,111 +18,31 @@ #include #include -#include -#include - #include -/* These values define a 720x576 anamorphic widescreen image that gets - * scaled to 1024x576 (16:9). */ -#define WIDTH 720 -#define HEIGHT 576 -#define PAR_NUM 64 -#define PAR_DEN 45 -#define FPS 25 -#define BITRATE 16*1024*1024 +#define WIDTH 1280 +#define HEIGHT 720 +#define FPS 30 int main(int argc, char *argv[]) { char file[1024]; - AVPacket packet; - AVFormatContext *fmt; - AVCodecContext *ctx; - AVStream *stream; - AVCodec *codec; - AVFrame *frame; - struct SwsContext *sws = NULL; pipi_image_t *image; + pipi_sequence_t *seq; pipi_pixels_t *p; - uint8_t *buffer, *tmp; - int f, len; + int f; if(argc < 2) return EXIT_FAILURE; - av_register_all(); - fmt = avformat_alloc_context(); - if(!fmt) - return EXIT_FAILURE; - snprintf(fmt->filename, sizeof(fmt->filename), "%s", argv[1]); - - fmt->oformat = guess_format(NULL, argv[1], NULL); - if(!fmt->oformat) - fmt->oformat = guess_format("mpeg", NULL, NULL); - if(!fmt->oformat) - return EXIT_FAILURE; - - stream = av_new_stream(fmt, 0); - if(!stream) - return EXIT_FAILURE; - stream->sample_aspect_ratio.num = PAR_NUM; - stream->sample_aspect_ratio.den = PAR_DEN; - - ctx = stream->codec; - ctx->width = WIDTH; - ctx->height = HEIGHT; - ctx->sample_aspect_ratio.num = PAR_NUM; - ctx->sample_aspect_ratio.den = PAR_DEN; - ctx->codec_id = fmt->oformat->video_codec; - //ctx->codec_id = CODEC_ID_FFV1; - //ctx->codec_id = CODEC_ID_THEORA; - //ctx->codec_id = CODEC_ID_SNOW; - //ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; /* Snow */ - //ctx->codec_id = CODEC_ID_MJPEG; - ctx->codec_type = CODEC_TYPE_VIDEO; - ctx->bit_rate = BITRATE; - ctx->time_base.num = 1; - ctx->time_base.den = FPS; /* 25 fps */ - ctx->gop_size = FPS / 2; - ctx->pix_fmt = PIX_FMT_YUV420P; /* send YUV 420 */ - if(ctx->codec_id == CODEC_ID_MPEG2VIDEO) - ctx->max_b_frames = 2; - if(ctx->codec_id == CODEC_ID_MPEG1VIDEO) - ctx->mb_decision = 2; - if(fmt->oformat->flags & AVFMT_GLOBALHEADER) - ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; - - if(av_set_parameters(fmt, NULL) < 0) - return EXIT_FAILURE; - - codec = avcodec_find_encoder(ctx->codec_id); - if(!codec) - return EXIT_FAILURE; - if(avcodec_open(ctx, codec) < 0) - return EXIT_FAILURE; - - frame = avcodec_alloc_frame(); - if(!frame) - return EXIT_FAILURE; - tmp = av_malloc(avpicture_get_size(ctx->pix_fmt, ctx->width, ctx->height)); - if(!tmp) + seq = pipi_open_sequence(argv[1], WIDTH, HEIGHT, FPS); + if(!seq) return EXIT_FAILURE; - avpicture_fill((AVPicture *)frame, tmp, ctx->pix_fmt, - ctx->width, ctx->height); - - if(!(fmt->flags & AVFMT_NOFILE)) - if(url_fopen(&fmt->pb, argv[1], URL_WRONLY) < 0) - return EXIT_FAILURE; - - len = 64 * 1024 * 1024; - buffer = av_malloc(len); - - av_write_header(fmt); for(f = 0; ; f++) { uint8_t *start; - int w = 0, h = 0, bytes, pitch; + int w = 0, h = 0; if(!fgets(file, sizeof(file), stdin)) break; @@ -133,61 +53,15 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; p = pipi_get_pixels(image, PIPI_PIXELS_RGBA_U8); start = (uint8_t *)p->pixels; - pitch = p->w * 4; - - if(w != p->w || h != p->h) - { - w = p->w; - h = p->h; - if(sws) - sws_freeContext(sws); - sws = NULL; - } - - if(!sws) - sws = sws_getContext(w, h, PIX_FMT_RGB32, ctx->width, ctx->height, - ctx->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL); - - sws_scale(sws, &start, &pitch, 0, h, frame->data, frame->linesize); - + pipi_feed_sequence(seq, start, p->w, p->h); pipi_free(image); - bytes = avcodec_encode_video(ctx, buffer, len, frame); - if(bytes <= 0) - continue; - - av_init_packet(&packet); - if(ctx->coded_frame->pts != (int64_t)AV_NOPTS_VALUE) - packet.pts = av_rescale_q(ctx->coded_frame->pts, - ctx->time_base, stream->time_base); - if(ctx->coded_frame->key_frame) - packet.flags |= PKT_FLAG_KEY; - packet.stream_index = 0; - packet.data = buffer; - packet.size = bytes; - if(av_interleaved_write_frame(fmt, &packet) < 0) - return EXIT_FAILURE; - fprintf(stderr, "frame %d\r", f); } fprintf(stderr, "\n"); - av_write_trailer(fmt); - - avcodec_close(stream->codec); - av_free(frame->data[0]); - av_free(frame); - av_free(buffer); - av_freep(&fmt->streams[0]->codec); - av_freep(&fmt->streams[0]); - - if(sws) - sws_freeContext(sws); - if(!(fmt->flags & AVFMT_NOFILE)) - url_fclose(fmt->pb); - - av_free(fmt); + pipi_close_sequence(seq); return EXIT_SUCCESS; } diff --git a/examples/makemovie.vcproj b/examples/makemovie.vcproj new file mode 100644 index 0000000..3ff7da3 --- /dev/null +++ b/examples/makemovie.vcproj @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/sharpen.vcproj b/examples/sharpen.vcproj new file mode 100644 index 0000000..40867e1 --- /dev/null +++ b/examples/sharpen.vcproj @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pipi/Makefile.am b/pipi/Makefile.am index db47c93..4b91346 100644 --- a/pipi/Makefile.am +++ b/pipi/Makefile.am @@ -28,6 +28,7 @@ libpipi_la_SOURCES = \ resize.c \ dither.c \ accessors.c \ + sequence.c \ $(codec_sources) \ $(paint_sources) \ $(render_sources) \ diff --git a/pipi/libpipi.vcproj b/pipi/libpipi.vcproj index 8a5c136..10db1a2 100644 --- a/pipi/libpipi.vcproj +++ b/pipi/libpipi.vcproj @@ -18,8 +18,8 @@ @@ -238,6 +242,10 @@ RelativePath=".\resize.c" > + + diff --git a/pipi/pipi.h b/pipi/pipi.h index 6db0327..3ff965a 100644 --- a/pipi/pipi.h +++ b/pipi/pipi.h @@ -1,6 +1,6 @@ /* * libpipi Pathetic image processing interface library - * Copyright (c) 2004-2008 Sam Hocevar + * Copyright (c) 2004-2009 Sam Hocevar * 2008 Jean-Yves Lamoureux + * Copyright (c) 2004-2009 Sam Hocevar * All Rights Reserved * * $Id$ @@ -82,6 +82,13 @@ struct pipi_image int (*codec_free)(pipi_image_t *); }; +struct pipi_sequence +{ + int w, h, fps; + + void *codec_priv; +}; + struct pipi_context { int nimages; diff --git a/pipi/sequence.c b/pipi/sequence.c new file mode 100644 index 0000000..5969a18 --- /dev/null +++ b/pipi/sequence.c @@ -0,0 +1,279 @@ +/* + * libpipi Pathetic image processing interface library + * Copyright (c) 2004-2009 Sam Hocevar + * All Rights Reserved + * + * $Id$ + * + * This library is free software. It comes without any warranty, to + * the extent permitted by applicable law. You can redistribute it + * and/or modify it under the terms of the Do What The Fuck You Want + * To Public License, Version 2, as published by Sam Hocevar. See + * http://sam.zoy.org/wtfpl/COPYING for more details. + */ + +/* + * codec.c: image I/O functions + */ + +#include "config.h" + +#if defined _WIN32 +# undef _CRT_SECURE_NO_WARNINGS +# define _CRT_SECURE_NO_WARNINGS /* I know how to use snprintf, thank you */ +# define snprintf _snprintf +#endif + +#include +#include +#include + +#if defined USE_FFMPEG +# include +# include +#endif + +#include "pipi.h" +#include "pipi_internals.h" + +#if defined USE_FFMPEG +typedef struct +{ + uint8_t *buf; + size_t buf_len; + + AVFormatContext *fmt_ctx; + AVStream *stream; + AVCodecContext *cod_ctx; + AVCodec *codec; + AVFrame *frame; + + struct SwsContext *sws_ctx; + int src_width, src_height; +} +ffmpeg_codec_t; +#endif + +#define PAR_NUM 1 +#define PAR_DEN 1 +#define BITRATE (16 * 1024 * 1024) + +pipi_sequence_t *pipi_open_sequence(char *file, int width, int height, int fps) +{ +#if defined USE_FFMPEG + static int initialised = 0; + + pipi_sequence_t *seq; + ffmpeg_codec_t *ff; + uint8_t *tmp; + + seq = malloc(sizeof(pipi_sequence_t)); + seq->w = width; + seq->h = height; + seq->fps = fps; + + ff = malloc(sizeof(ffmpeg_codec_t)); + memset(ff, 0, sizeof(*ff)); + + seq->codec_priv = ff; + + if (!initialised) + { + av_register_all(); + initialised = 1; + } + + ff->fmt_ctx = avformat_alloc_context(); + if (!ff->fmt_ctx) + goto error; + + /* Careful here: the Win32 snprintf doesn't seem to add a trailing + * zero to the truncated output. */ + snprintf(ff->fmt_ctx->filename, sizeof(ff->fmt_ctx->filename), + file); + ff->fmt_ctx->filename[sizeof(ff->fmt_ctx->filename) - 1] = '\0'; + + ff->fmt_ctx->oformat = guess_format(NULL, file, NULL); + if (!ff->fmt_ctx->oformat) + ff->fmt_ctx->oformat = guess_format("mpeg", NULL, NULL); + if (!ff->fmt_ctx->oformat) + goto error; + + ff->stream = av_new_stream(ff->fmt_ctx, 0); + if (!ff->stream) + goto error; + + ff->stream->sample_aspect_ratio.num = PAR_NUM; + ff->stream->sample_aspect_ratio.den = PAR_DEN; + + ff->cod_ctx = ff->stream->codec; + + ff->cod_ctx->width = width; + ff->cod_ctx->height = height; + ff->cod_ctx->sample_aspect_ratio.num = PAR_NUM; + ff->cod_ctx->sample_aspect_ratio.den = PAR_DEN; + ff->cod_ctx->codec_id = ff->fmt_ctx->oformat->video_codec; + ff->cod_ctx->codec_type = CODEC_TYPE_VIDEO; + ff->cod_ctx->bit_rate = BITRATE; + ff->cod_ctx->time_base.num = 1; + ff->cod_ctx->time_base.den = fps; + ff->cod_ctx->gop_size = fps * 3 / 4; /* empirical */ + ff->cod_ctx->pix_fmt = PIX_FMT_YUV420P; /* send YUV 420 */ + if (ff->cod_ctx->codec_id == CODEC_ID_MPEG2VIDEO) + ff->cod_ctx->max_b_frames = 2; + if (ff->cod_ctx->codec_id == CODEC_ID_MPEG1VIDEO) + ff->cod_ctx->mb_decision = 2; + if (ff->fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) + ff->cod_ctx->flags |= CODEC_FLAG_GLOBAL_HEADER; + + if (av_set_parameters(ff->fmt_ctx, NULL) < 0) + goto error; + + ff->codec = avcodec_find_encoder(ff->cod_ctx->codec_id); + if (!ff->codec) + goto error; + if (avcodec_open(ff->cod_ctx, ff->codec) < 0) + goto error; + + ff->frame = avcodec_alloc_frame(); + if (!ff->frame) + goto error; + tmp = (uint8_t *)av_malloc(avpicture_get_size(ff->cod_ctx->pix_fmt, + ff->cod_ctx->width, + ff->cod_ctx->height)); + if (!tmp) + goto error; + avpicture_fill((AVPicture *)ff->frame, tmp, ff->cod_ctx->pix_fmt, + ff->cod_ctx->width, ff->cod_ctx->height); + + if (!(ff->fmt_ctx->flags & AVFMT_NOFILE)) + if (url_fopen(&ff->fmt_ctx->pb, file, URL_WRONLY) < 0) + goto error; + + ff->buf_len = 64 * 1024 * 1024; + ff->buf = (uint8_t *)av_malloc(ff->buf_len); + + av_write_header(ff->fmt_ctx); + + return seq; + +error: + pipi_close_sequence(seq); + return NULL; + +#else + return NULL; + +#endif +} + +int pipi_feed_sequence(pipi_sequence_t *seq, uint8_t *buffer, int width, int height) +{ +#if defined USE_FFMPEG + AVPacket packet; + size_t bytes; + int pitch; + + ffmpeg_codec_t *ff = (ffmpeg_codec_t *)seq->codec_priv; + + if (ff->src_width != width || ff->src_height != height) + { + ff->src_width = width; + ff->src_height = height; + if (ff->sws_ctx) + sws_freeContext(ff->sws_ctx); + ff->sws_ctx = NULL; + } + + if (!ff->sws_ctx) + ff->sws_ctx = sws_getContext(width, height, PIX_FMT_RGB32, + ff->cod_ctx->width, + ff->cod_ctx->height, + ff->cod_ctx->pix_fmt, SWS_BICUBIC, + NULL, NULL, NULL); + if (!ff->sws_ctx) + return -1; + + pitch = width * 4; + sws_scale(ff->sws_ctx, &buffer, &pitch, 0, height, + ff->frame->data, ff->frame->linesize); + + bytes = avcodec_encode_video(ff->cod_ctx, ff->buf, + ff->buf_len, ff->frame); + if (bytes <= 0) + return 0; + + av_init_packet(&packet); + if (ff->cod_ctx->coded_frame->pts != 0x8000000000000000LL) + packet.pts = av_rescale_q(ff->cod_ctx->coded_frame->pts, + ff->cod_ctx->time_base, ff->stream->time_base); + if (ff->cod_ctx->coded_frame->key_frame) + packet.flags |= PKT_FLAG_KEY; + packet.stream_index = 0; + packet.data = ff->buf; + packet.size = bytes; + + if (av_interleaved_write_frame(ff->fmt_ctx, &packet) < 0) + return -1; +#endif + + return 0; +} + +int pipi_close_sequence(pipi_sequence_t *seq) +{ +#if defined USE_FFMPEG + ffmpeg_codec_t *ff = (ffmpeg_codec_t *)seq->codec_priv; + + if (ff->fmt_ctx) + { + av_write_trailer(ff->fmt_ctx); + } + + if (ff->buf) + { + av_free(ff->buf); + ff->buf = NULL; + } + + if (ff->cod_ctx) + { + avcodec_close(ff->cod_ctx); + ff->cod_ctx = NULL; + } + + if (ff->frame) + { + av_free(ff->frame->data[0]); + av_free(ff->frame); + ff->frame = NULL; + } + + if (ff->fmt_ctx) + { + av_freep(&ff->fmt_ctx->streams[0]->codec); + ff->codec = NULL; + + av_freep(&ff->fmt_ctx->streams[0]); + ff->stream = NULL; + + if (!(ff->fmt_ctx->flags & AVFMT_NOFILE)) + url_fclose(ff->fmt_ctx->pb); + + av_free(ff->fmt_ctx); + ff->fmt_ctx = NULL; + } + + if (ff->sws_ctx) + { + sws_freeContext(ff->sws_ctx); + ff->sws_ctx = NULL; + ff->src_width = ff->src_height = 0; + } + + free(ff); + free(seq); +#endif + + return 0; +} diff --git a/win32/config.h b/win32/config.h index 17f139d..623cf12 100644 --- a/win32/config.h +++ b/win32/config.h @@ -43,6 +43,7 @@ #define USE_GDI 1 #define USE_GDIPLUS 1 /* #undef USE_COCOA */ +#define USE_FFMPEG 1 /* #undef const */ #ifndef __cplusplus #define inline __inline