|
|
@@ -0,0 +1,185 @@ |
|
|
|
/* |
|
|
|
* makemovie read image names from stdin and create a movie |
|
|
|
* Copyright (c) 2009 Sam Hocevar <sam@zoy.org> |
|
|
|
* All Rights Reserved |
|
|
|
* |
|
|
|
* $Id$ |
|
|
|
* |
|
|
|
* This program 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. |
|
|
|
*/ |
|
|
|
|
|
|
|
#include "config.h" |
|
|
|
|
|
|
|
#include <stdlib.h> |
|
|
|
#include <stdio.h> |
|
|
|
#include <string.h> |
|
|
|
|
|
|
|
#include <avformat.h> |
|
|
|
#include <swscale.h> |
|
|
|
|
|
|
|
#include <pipi.h> |
|
|
|
|
|
|
|
/* 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 8000000 |
|
|
|
|
|
|
|
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_pixels_t *p; |
|
|
|
uint8_t *buffer, *tmp; |
|
|
|
int len; |
|
|
|
|
|
|
|
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_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) |
|
|
|
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(;;) |
|
|
|
{ |
|
|
|
uint8_t *start; |
|
|
|
int w = 0, h = 0, bytes, pitch; |
|
|
|
|
|
|
|
if(!fgets(file, sizeof(file), stdin)) |
|
|
|
break; |
|
|
|
file[strlen(file) - 1] = '\0'; |
|
|
|
|
|
|
|
image = pipi_load(file); |
|
|
|
if(!image) |
|
|
|
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_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; |
|
|
|
} |
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
return EXIT_SUCCESS; |
|
|
|
} |
|
|
|
|