/*
 *  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 <libavformat/avformat.h>
#include <libswscale/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 16*1024*1024

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 f, 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_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)
        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;

        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;

        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);

    return EXIT_SUCCESS;
}