Mercurial > hg > aimc
diff trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovieDirect.cc @ 397:7a573750b186
- First add of a lot of graphics code from the old version. Not working yet, not even compiling yet.
author | tomwalters |
---|---|
date | Fri, 15 Oct 2010 05:40:53 +0000 |
parents | |
children | 3ee03a6b95a0 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/trunk/src/Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovieDirect.cc Fri Oct 15 05:40:53 2010 +0000 @@ -0,0 +1,396 @@ +// Copyright 2007, Thomas Walters +// +// AIM-C: A C++ implementation of the Auditory Image Model +// http://www.acousticscale.org/AIMC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! + * \file + * \brief Output device for output direct to a movie via local calls to libavcodec + * + * \author Tom Walters <tom@acousticscale.org> + * \date created 2007/10/05 + * \version \$Id: $ + */ + +#include "Support/Common.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> + +#include "Support/util.h" +#include "Output/GraphicsOutputDeviceMovieDirect.h" + +GraphicsOutputDeviceMovieDirect::GraphicsOutputDeviceMovieDirect(Parameters *params) + : GraphicsOutputDeviceMovie(params) { + m_sMovieFile[0] = '\0'; + m_sSoundFile[0] = '\0'; +} + +bool GraphicsOutputDeviceMovieDirect::Initialize(const char *sSoundFile, + const char *sMovieFile) { + // We want pnm for direct movie conversion as the data format is nice and simple + //! \bug This may change the user preference in GUI, hmm what to do? See TODO.txt + //m_pParam->SetString("output.img.format", "pnm"); + + // Initialise GraphicsOutputDevicePlotutils for memory buffer use + if(!GraphicsOutputDeviceCairo::Initialize()) + return false; + + int width = m_pParam->GetUInt("output.img.width"); + int height = m_pParam->GetUInt("output.img.height"); + //float framerate = 1000.0f/m_pParam->GetFloat("output.frameperiod"); + float framerate=1000.0f/20.0f; + + m_pOutputMovie = new LibavformatWriter; + m_pOutputMovie->Init(sMovieFile, width, height, framerate); + + return true; +} + +void GraphicsOutputDeviceMovieDirect::Stop() { + // Make sure Plotutils is really done writing. + GraphicsOutputDeviceCairo::Stop(); + m_pOutputMovie->End(); + delete m_pOutputMovie; + +} + +void GraphicsOutputDeviceMovieDirect::gRelease() { + // write the buffer + unsigned char *buf = GraphicsOutputDeviceCairo::GetBuffer(); + + if (buf != NULL) + m_pOutputMovie->WriteFrame(buf); + + GraphicsOutputDeviceCairo::gRelease(); +} + + +// Everything below here is hacked from the Libavformat API example: +/* + * Libavformat API example: Output a media file in any supported + * libavformat format. The default codecs are used. + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +LibavformatWriter::LibavformatWriter() { + sws_flags = SWS_BICUBIC; + pixfmt= PIX_FMT_RGB24; + fmt = NULL; +} + +bool LibavformatWriter::Init(const char *sMovieFile, + int width, + int height, + float framerate) { + /* initialize libavcodec, and register all codecs and formats */ + av_register_all(); + + /* auto detect the output format from the name. default is mpeg. */ + fmt = guess_format(NULL, sMovieFile, NULL); + if (!fmt) { + printf("Could not deduce output format from file extension: using MPEG.\n"); + fmt = guess_format("mpeg", NULL, NULL); + } + if (!fmt) { + fprintf(stderr, "Could not find suitable output format\n"); + return false; + } + + /* allocate the output media context */ + oc = av_alloc_format_context(); + if (!oc) { + fprintf(stderr, "Memory error\n"); + return false; + } + oc->oformat = fmt; + snprintf(oc->filename, sizeof(oc->filename), "%s", sMovieFile); + + /* add the audio and video streams using the default format codecs + and initialize the codecs */ + video_st = NULL; + fmt->video_codec=CODEC_ID_PNG; + if (fmt->video_codec != CODEC_ID_NONE) { + video_st = add_video_stream(oc, fmt->video_codec, width, height, framerate); + } + + /* set the output parameters (must be done even if no + parameters). */ + if (av_set_parameters(oc, NULL) < 0) { + fprintf(stderr, "Invalid output format parameters\n"); + return false; + } + + dump_format(oc, 0, sMovieFile, 1); + + /* now that all the parameters are set, we can open the audio and + video codecs and allocate the necessary encode buffers */ + if (video_st) { + open_video(oc, video_st); + } + + /* open the output file, if needed */ + if (!(fmt->flags & AVFMT_NOFILE)) { + if (url_fopen(&oc->pb, sMovieFile, URL_WRONLY) < 0) { + fprintf(stderr, "Could not open '%s'\n", sMovieFile); + return false; + } + } + + /* write the stream header, if any */ + av_write_header(oc); + return true; +} + + +/**************************************************************/ +/* video output */ + +/* add a video output stream */ +AVStream* LibavformatWriter::add_video_stream(AVFormatContext *oc, + CodecID codec_id, + int width, + int height, + float framerate) { + AVCodecContext *c; + AVStream *st; + + st = av_new_stream(oc, 0); + if (!st) { + fprintf(stderr, "Could not alloc stream\n"); + return NULL; + } + + c = st->codec; + c->codec_id = codec_id; + c->codec_type = CODEC_TYPE_VIDEO; + + /* put sample parameters */ + /* resolution must be a multiple of two */ + c->width = width; + c->height = height; + /* time base: this is the fundamental unit of time (in seconds) in terms + of which frame timestamps are represented. for fixed-fps content, + timebase should be 1/framerate and timestamp increments should be + identically 1. */ + c->time_base.den = (int)framerate; + c->time_base.num = 1; + c->gop_size = 12; /* emit one intra frame every twelve frames at most */ + c->pix_fmt = pixfmt; + // some formats want stream headers to be separate + if(!strcmp(oc->oformat->name, "mp4") + || !strcmp(oc->oformat->name, "mov") + || !strcmp(oc->oformat->name, "3gp")) { + c->flags |= CODEC_FLAG_GLOBAL_HEADER; + } + return st; +} + +AVFrame* LibavformatWriter::alloc_picture(int pix_fmt, + int width, + int height) { + AVFrame *picture; + uint8_t *picture_buf; + int size; + + picture = avcodec_alloc_frame(); + if (!picture) { + return NULL; + } + size = avpicture_get_size(pix_fmt, width, height); + picture_buf = (uint8_t*)av_malloc(size); + if (!picture_buf) { + av_free(picture); + return NULL; + } + avpicture_fill((AVPicture *)picture, picture_buf, + pix_fmt, width, height); + return picture; +} + +void LibavformatWriter::open_video(AVFormatContext *oc, AVStream *st) { + AVCodec *codec; + AVCodecContext *c; + c = st->codec; + + /* find the video encoder */ + codec = avcodec_find_encoder(c->codec_id); + if (!codec) { + fprintf(stderr, "codec not found\n"); + exit(1); + } + + /* open the codec */ + if (avcodec_open(c, codec) < 0) { + fprintf(stderr, "could not open codec\n"); + exit(1); + } + + video_outbuf = NULL; + if (!(oc->oformat->flags & AVFMT_RAWPICTURE)) { + /* allocate output buffer */ + /* XXX: API change will be done */ + /* buffers passed into lav* can be allocated any way you prefer, + as long as they're aligned enough for the architecture, and + they're freed appropriately (such as using av_free for buffers + allocated with av_malloc) */ + video_outbuf_size = 200000; + video_outbuf = (uint8_t*)av_malloc(video_outbuf_size); + } + + /* allocate the encoded raw picture */ + picture = alloc_picture(c->pix_fmt, c->width, c->height); + if (!picture) { + fprintf(stderr, "Could not allocate picture\n"); + exit(1); + } + + /* if the output format is not RGB32, then a temporary RGB32 + picture is needed too. It is then converted to the required + output format */ + tmp_picture = NULL; + if (c->pix_fmt != PIX_FMT_RGB32) { + tmp_picture = alloc_picture(PIX_FMT_RGB32, c->width, c->height); + if (!tmp_picture) { + fprintf(stderr, "Could not allocate temporary picture\n"); + exit(1); + } + } +} + + +void LibavformatWriter::close_video(AVFormatContext *oc, + AVStream *st) { + avcodec_close(st->codec); + av_free(picture->data[0]); + av_free(picture); + if (tmp_picture) { + av_free(tmp_picture->data[0]); + av_free(tmp_picture); + } + av_free(video_outbuf); +} + +void LibavformatWriter::WriteFrame(unsigned char *pFrameBuffer) { + /* compute current video time */ + if (video_st) + video_pts = (double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den; + else + video_pts = 0.0; + + int out_size, ret; + AVCodecContext *c; + static struct SwsContext *img_convert_ctx; + c = video_st->codec; + if (c->pix_fmt != PIX_FMT_RGB32) { + /* as we only generate a RGB32 picture, we must convert it + to the codec pixel format if needed */ + if (img_convert_ctx == NULL) { + img_convert_ctx = sws_getContext(c->width, + c->height, + PIX_FMT_RGB32, + c->width, + c->height, + c->pix_fmt, + sws_flags, + NULL, + NULL, + NULL); + if (img_convert_ctx == NULL) { + fprintf(stderr, "Cannot initialize the conversion context\n"); + exit(1); + } + } + fill_image(tmp_picture, pFrameBuffer, c->width, c->height); + sws_scale(img_convert_ctx, tmp_picture->data, tmp_picture->linesize, + 0, c->height, picture->data, picture->linesize); + } else { + fill_image(picture, pFrameBuffer, c->width, c->height); + } + /* encode the image */ + out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture); + /* if zero size, it means the image was buffered */ + if (out_size > 0) { + AVPacket pkt; + av_init_packet(&pkt); + + pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, video_st->time_base); + if(c->coded_frame->key_frame) { + pkt.flags |= PKT_FLAG_KEY; + pkt.stream_index= video_st->index; + pkt.data= video_outbuf; + pkt.size= out_size; + /* write the compressed frame in the media file */ + ret = av_write_frame(oc, &pkt); + } + } else { + ret = 0; + } + if (ret != 0) { + fprintf(stderr, "Error while writing video frame\n"); + exit(1); + } +} + +void LibavformatWriter::End() { + /* close each codec */ + if (video_st) { + close_video(oc, video_st); + } + /* write the trailer, if any */ + av_write_trailer(oc); + + /* free the streams */ + for(i = 0; i < oc->nb_streams; i++) { + av_freep(&oc->streams[i]->codec); + av_freep(&oc->streams[i]); + } + + if (!(fmt->flags & AVFMT_NOFILE)) { + /* close the output file */ + url_fclose(&oc->pb); + } + /* free the stream */ + av_free(oc); +} + +void LibavformatWriter::fill_image(AVFrame *pict, + unsigned char *pFrameBuffer, + int width, + int height) { + memcpy((void*)&(pict->data[0][0]), (void*)pFrameBuffer, width*height*4); +}