annotate src/Modules/Output/Graphics/Devices/GraphicsOutputDeviceMovieDirect.cc @ 117:c5ac2f0c7fc5

- All \t to two spaces (style guide compliance)
author tomwalters
date Fri, 15 Oct 2010 05:46:53 +0000
parents 47b009f2c936
children 9d880fb93c39
rev   line source
tomwalters@116 1 // Copyright 2007, Thomas Walters
tomwalters@116 2 //
tomwalters@116 3 // AIM-C: A C++ implementation of the Auditory Image Model
tomwalters@116 4 // http://www.acousticscale.org/AIMC
tomwalters@116 5 //
tomwalters@116 6 // Licensed under the Apache License, Version 2.0 (the "License");
tomwalters@116 7 // you may not use this file except in compliance with the License.
tomwalters@116 8 // You may obtain a copy of the License at
tomwalters@116 9 //
tomwalters@116 10 // http://www.apache.org/licenses/LICENSE-2.0
tomwalters@116 11 //
tomwalters@116 12 // Unless required by applicable law or agreed to in writing, software
tomwalters@116 13 // distributed under the License is distributed on an "AS IS" BASIS,
tomwalters@116 14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
tomwalters@116 15 // See the License for the specific language governing permissions and
tomwalters@116 16 // limitations under the License.
tomwalters@116 17
tomwalters@116 18 /*!
tomwalters@116 19 * \file
tomwalters@116 20 * \brief Output device for output direct to a movie via local calls to libavcodec
tomwalters@116 21 *
tomwalters@116 22 * \author Tom Walters <tom@acousticscale.org>
tomwalters@116 23 * \date created 2007/10/05
tomwalters@116 24 * \version \$Id: $
tomwalters@116 25 */
tomwalters@116 26
tomwalters@116 27 #include "Support/Common.h"
tomwalters@116 28
tomwalters@116 29 #include <stdlib.h>
tomwalters@116 30 #include <stdio.h>
tomwalters@116 31 #include <string.h>
tomwalters@116 32 #include <math.h>
tomwalters@116 33
tomwalters@116 34 #include "Support/util.h"
tomwalters@116 35 #include "Output/GraphicsOutputDeviceMovieDirect.h"
tomwalters@116 36
tomwalters@116 37 GraphicsOutputDeviceMovieDirect::GraphicsOutputDeviceMovieDirect(Parameters *params)
tomwalters@117 38 : GraphicsOutputDeviceMovie(params) {
tomwalters@117 39 m_sMovieFile[0] = '\0';
tomwalters@117 40 m_sSoundFile[0] = '\0';
tomwalters@116 41 }
tomwalters@116 42
tomwalters@116 43 bool GraphicsOutputDeviceMovieDirect::Initialize(const char *sSoundFile,
tomwalters@116 44 const char *sMovieFile) {
tomwalters@117 45 // We want pnm for direct movie conversion as the data format is nice and simple
tomwalters@117 46 //! \bug This may change the user preference in GUI, hmm what to do? See TODO.txt
tomwalters@117 47 //m_pParam->SetString("output.img.format", "pnm");
tomwalters@116 48
tomwalters@117 49 // Initialise GraphicsOutputDevicePlotutils for memory buffer use
tomwalters@117 50 if(!GraphicsOutputDeviceCairo::Initialize())
tomwalters@116 51 return false;
tomwalters@116 52
tomwalters@116 53 int width = m_pParam->GetUInt("output.img.width");
tomwalters@117 54 int height = m_pParam->GetUInt("output.img.height");
tomwalters@117 55 //float framerate = 1000.0f/m_pParam->GetFloat("output.frameperiod");
tomwalters@117 56 float framerate=1000.0f/20.0f;
tomwalters@116 57
tomwalters@116 58 m_pOutputMovie = new LibavformatWriter;
tomwalters@117 59 m_pOutputMovie->Init(sMovieFile, width, height, framerate);
tomwalters@116 60
tomwalters@117 61 return true;
tomwalters@116 62 }
tomwalters@116 63
tomwalters@116 64 void GraphicsOutputDeviceMovieDirect::Stop() {
tomwalters@117 65 // Make sure Plotutils is really done writing.
tomwalters@117 66 GraphicsOutputDeviceCairo::Stop();
tomwalters@117 67 m_pOutputMovie->End();
tomwalters@117 68 delete m_pOutputMovie;
tomwalters@116 69
tomwalters@116 70 }
tomwalters@116 71
tomwalters@116 72 void GraphicsOutputDeviceMovieDirect::gRelease() {
tomwalters@116 73 // write the buffer
tomwalters@116 74 unsigned char *buf = GraphicsOutputDeviceCairo::GetBuffer();
tomwalters@116 75
tomwalters@116 76 if (buf != NULL)
tomwalters@116 77 m_pOutputMovie->WriteFrame(buf);
tomwalters@116 78
tomwalters@116 79 GraphicsOutputDeviceCairo::gRelease();
tomwalters@116 80 }
tomwalters@116 81
tomwalters@116 82
tomwalters@116 83 // Everything below here is hacked from the Libavformat API example:
tomwalters@116 84 /*
tomwalters@116 85 * Libavformat API example: Output a media file in any supported
tomwalters@116 86 * libavformat format. The default codecs are used.
tomwalters@116 87 *
tomwalters@116 88 * Copyright (c) 2003 Fabrice Bellard
tomwalters@116 89 *
tomwalters@116 90 * Permission is hereby granted, free of charge, to any person obtaining a copy
tomwalters@116 91 * of this software and associated documentation files (the "Software"), to deal
tomwalters@116 92 * in the Software without restriction, including without limitation the rights
tomwalters@116 93 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
tomwalters@116 94 * copies of the Software, and to permit persons to whom the Software is
tomwalters@116 95 * furnished to do so, subject to the following conditions:
tomwalters@116 96 *
tomwalters@116 97 * The above copyright notice and this permission notice shall be included in
tomwalters@116 98 * all copies or substantial portions of the Software.
tomwalters@116 99 *
tomwalters@116 100 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
tomwalters@116 101 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
tomwalters@116 102 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
tomwalters@116 103 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
tomwalters@116 104 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
tomwalters@116 105 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
tomwalters@116 106 * THE SOFTWARE.
tomwalters@116 107 */
tomwalters@116 108
tomwalters@116 109
tomwalters@116 110 LibavformatWriter::LibavformatWriter() {
tomwalters@116 111 sws_flags = SWS_BICUBIC;
tomwalters@116 112 pixfmt= PIX_FMT_RGB24;
tomwalters@116 113 fmt = NULL;
tomwalters@116 114 }
tomwalters@116 115
tomwalters@116 116 bool LibavformatWriter::Init(const char *sMovieFile,
tomwalters@116 117 int width,
tomwalters@116 118 int height,
tomwalters@116 119 float framerate) {
tomwalters@116 120 /* initialize libavcodec, and register all codecs and formats */
tomwalters@116 121 av_register_all();
tomwalters@116 122
tomwalters@116 123 /* auto detect the output format from the name. default is mpeg. */
tomwalters@116 124 fmt = guess_format(NULL, sMovieFile, NULL);
tomwalters@116 125 if (!fmt) {
tomwalters@116 126 printf("Could not deduce output format from file extension: using MPEG.\n");
tomwalters@116 127 fmt = guess_format("mpeg", NULL, NULL);
tomwalters@116 128 }
tomwalters@116 129 if (!fmt) {
tomwalters@116 130 fprintf(stderr, "Could not find suitable output format\n");
tomwalters@116 131 return false;
tomwalters@116 132 }
tomwalters@116 133
tomwalters@116 134 /* allocate the output media context */
tomwalters@116 135 oc = av_alloc_format_context();
tomwalters@116 136 if (!oc) {
tomwalters@116 137 fprintf(stderr, "Memory error\n");
tomwalters@116 138 return false;
tomwalters@116 139 }
tomwalters@116 140 oc->oformat = fmt;
tomwalters@116 141 snprintf(oc->filename, sizeof(oc->filename), "%s", sMovieFile);
tomwalters@116 142
tomwalters@116 143 /* add the audio and video streams using the default format codecs
tomwalters@116 144 and initialize the codecs */
tomwalters@116 145 video_st = NULL;
tomwalters@116 146 fmt->video_codec=CODEC_ID_PNG;
tomwalters@116 147 if (fmt->video_codec != CODEC_ID_NONE) {
tomwalters@116 148 video_st = add_video_stream(oc, fmt->video_codec, width, height, framerate);
tomwalters@116 149 }
tomwalters@116 150
tomwalters@116 151 /* set the output parameters (must be done even if no
tomwalters@116 152 parameters). */
tomwalters@116 153 if (av_set_parameters(oc, NULL) < 0) {
tomwalters@116 154 fprintf(stderr, "Invalid output format parameters\n");
tomwalters@116 155 return false;
tomwalters@116 156 }
tomwalters@116 157
tomwalters@116 158 dump_format(oc, 0, sMovieFile, 1);
tomwalters@116 159
tomwalters@116 160 /* now that all the parameters are set, we can open the audio and
tomwalters@116 161 video codecs and allocate the necessary encode buffers */
tomwalters@116 162 if (video_st) {
tomwalters@116 163 open_video(oc, video_st);
tomwalters@116 164 }
tomwalters@116 165
tomwalters@116 166 /* open the output file, if needed */
tomwalters@116 167 if (!(fmt->flags & AVFMT_NOFILE)) {
tomwalters@116 168 if (url_fopen(&oc->pb, sMovieFile, URL_WRONLY) < 0) {
tomwalters@116 169 fprintf(stderr, "Could not open '%s'\n", sMovieFile);
tomwalters@116 170 return false;
tomwalters@116 171 }
tomwalters@116 172 }
tomwalters@116 173
tomwalters@116 174 /* write the stream header, if any */
tomwalters@116 175 av_write_header(oc);
tomwalters@116 176 return true;
tomwalters@116 177 }
tomwalters@116 178
tomwalters@116 179
tomwalters@116 180 /**************************************************************/
tomwalters@116 181 /* video output */
tomwalters@116 182
tomwalters@116 183 /* add a video output stream */
tomwalters@116 184 AVStream* LibavformatWriter::add_video_stream(AVFormatContext *oc,
tomwalters@116 185 CodecID codec_id,
tomwalters@116 186 int width,
tomwalters@116 187 int height,
tomwalters@116 188 float framerate) {
tomwalters@116 189 AVCodecContext *c;
tomwalters@116 190 AVStream *st;
tomwalters@116 191
tomwalters@116 192 st = av_new_stream(oc, 0);
tomwalters@116 193 if (!st) {
tomwalters@116 194 fprintf(stderr, "Could not alloc stream\n");
tomwalters@116 195 return NULL;
tomwalters@116 196 }
tomwalters@116 197
tomwalters@116 198 c = st->codec;
tomwalters@116 199 c->codec_id = codec_id;
tomwalters@116 200 c->codec_type = CODEC_TYPE_VIDEO;
tomwalters@116 201
tomwalters@116 202 /* put sample parameters */
tomwalters@116 203 /* resolution must be a multiple of two */
tomwalters@116 204 c->width = width;
tomwalters@116 205 c->height = height;
tomwalters@116 206 /* time base: this is the fundamental unit of time (in seconds) in terms
tomwalters@116 207 of which frame timestamps are represented. for fixed-fps content,
tomwalters@116 208 timebase should be 1/framerate and timestamp increments should be
tomwalters@116 209 identically 1. */
tomwalters@116 210 c->time_base.den = (int)framerate;
tomwalters@116 211 c->time_base.num = 1;
tomwalters@116 212 c->gop_size = 12; /* emit one intra frame every twelve frames at most */
tomwalters@116 213 c->pix_fmt = pixfmt;
tomwalters@116 214 // some formats want stream headers to be separate
tomwalters@116 215 if(!strcmp(oc->oformat->name, "mp4")
tomwalters@116 216 || !strcmp(oc->oformat->name, "mov")
tomwalters@116 217 || !strcmp(oc->oformat->name, "3gp")) {
tomwalters@116 218 c->flags |= CODEC_FLAG_GLOBAL_HEADER;
tomwalters@116 219 }
tomwalters@116 220 return st;
tomwalters@116 221 }
tomwalters@116 222
tomwalters@116 223 AVFrame* LibavformatWriter::alloc_picture(int pix_fmt,
tomwalters@116 224 int width,
tomwalters@116 225 int height) {
tomwalters@116 226 AVFrame *picture;
tomwalters@116 227 uint8_t *picture_buf;
tomwalters@116 228 int size;
tomwalters@116 229
tomwalters@116 230 picture = avcodec_alloc_frame();
tomwalters@116 231 if (!picture) {
tomwalters@116 232 return NULL;
tomwalters@116 233 }
tomwalters@116 234 size = avpicture_get_size(pix_fmt, width, height);
tomwalters@116 235 picture_buf = (uint8_t*)av_malloc(size);
tomwalters@116 236 if (!picture_buf) {
tomwalters@116 237 av_free(picture);
tomwalters@116 238 return NULL;
tomwalters@116 239 }
tomwalters@116 240 avpicture_fill((AVPicture *)picture, picture_buf,
tomwalters@116 241 pix_fmt, width, height);
tomwalters@116 242 return picture;
tomwalters@116 243 }
tomwalters@116 244
tomwalters@116 245 void LibavformatWriter::open_video(AVFormatContext *oc, AVStream *st) {
tomwalters@116 246 AVCodec *codec;
tomwalters@116 247 AVCodecContext *c;
tomwalters@116 248 c = st->codec;
tomwalters@116 249
tomwalters@116 250 /* find the video encoder */
tomwalters@116 251 codec = avcodec_find_encoder(c->codec_id);
tomwalters@116 252 if (!codec) {
tomwalters@116 253 fprintf(stderr, "codec not found\n");
tomwalters@116 254 exit(1);
tomwalters@116 255 }
tomwalters@116 256
tomwalters@116 257 /* open the codec */
tomwalters@116 258 if (avcodec_open(c, codec) < 0) {
tomwalters@116 259 fprintf(stderr, "could not open codec\n");
tomwalters@116 260 exit(1);
tomwalters@116 261 }
tomwalters@116 262
tomwalters@116 263 video_outbuf = NULL;
tomwalters@116 264 if (!(oc->oformat->flags & AVFMT_RAWPICTURE)) {
tomwalters@116 265 /* allocate output buffer */
tomwalters@116 266 /* XXX: API change will be done */
tomwalters@116 267 /* buffers passed into lav* can be allocated any way you prefer,
tomwalters@116 268 as long as they're aligned enough for the architecture, and
tomwalters@116 269 they're freed appropriately (such as using av_free for buffers
tomwalters@116 270 allocated with av_malloc) */
tomwalters@116 271 video_outbuf_size = 200000;
tomwalters@116 272 video_outbuf = (uint8_t*)av_malloc(video_outbuf_size);
tomwalters@116 273 }
tomwalters@116 274
tomwalters@116 275 /* allocate the encoded raw picture */
tomwalters@116 276 picture = alloc_picture(c->pix_fmt, c->width, c->height);
tomwalters@116 277 if (!picture) {
tomwalters@116 278 fprintf(stderr, "Could not allocate picture\n");
tomwalters@116 279 exit(1);
tomwalters@116 280 }
tomwalters@116 281
tomwalters@116 282 /* if the output format is not RGB32, then a temporary RGB32
tomwalters@116 283 picture is needed too. It is then converted to the required
tomwalters@116 284 output format */
tomwalters@116 285 tmp_picture = NULL;
tomwalters@116 286 if (c->pix_fmt != PIX_FMT_RGB32) {
tomwalters@116 287 tmp_picture = alloc_picture(PIX_FMT_RGB32, c->width, c->height);
tomwalters@116 288 if (!tmp_picture) {
tomwalters@116 289 fprintf(stderr, "Could not allocate temporary picture\n");
tomwalters@116 290 exit(1);
tomwalters@116 291 }
tomwalters@116 292 }
tomwalters@116 293 }
tomwalters@116 294
tomwalters@116 295
tomwalters@116 296 void LibavformatWriter::close_video(AVFormatContext *oc,
tomwalters@116 297 AVStream *st) {
tomwalters@116 298 avcodec_close(st->codec);
tomwalters@116 299 av_free(picture->data[0]);
tomwalters@116 300 av_free(picture);
tomwalters@116 301 if (tmp_picture) {
tomwalters@116 302 av_free(tmp_picture->data[0]);
tomwalters@116 303 av_free(tmp_picture);
tomwalters@116 304 }
tomwalters@116 305 av_free(video_outbuf);
tomwalters@116 306 }
tomwalters@116 307
tomwalters@116 308 void LibavformatWriter::WriteFrame(unsigned char *pFrameBuffer) {
tomwalters@116 309 /* compute current video time */
tomwalters@116 310 if (video_st)
tomwalters@116 311 video_pts = (double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den;
tomwalters@116 312 else
tomwalters@116 313 video_pts = 0.0;
tomwalters@116 314
tomwalters@116 315 int out_size, ret;
tomwalters@116 316 AVCodecContext *c;
tomwalters@116 317 static struct SwsContext *img_convert_ctx;
tomwalters@116 318 c = video_st->codec;
tomwalters@116 319 if (c->pix_fmt != PIX_FMT_RGB32) {
tomwalters@116 320 /* as we only generate a RGB32 picture, we must convert it
tomwalters@116 321 to the codec pixel format if needed */
tomwalters@116 322 if (img_convert_ctx == NULL) {
tomwalters@116 323 img_convert_ctx = sws_getContext(c->width,
tomwalters@116 324 c->height,
tomwalters@116 325 PIX_FMT_RGB32,
tomwalters@116 326 c->width,
tomwalters@116 327 c->height,
tomwalters@116 328 c->pix_fmt,
tomwalters@116 329 sws_flags,
tomwalters@116 330 NULL,
tomwalters@116 331 NULL,
tomwalters@116 332 NULL);
tomwalters@116 333 if (img_convert_ctx == NULL) {
tomwalters@116 334 fprintf(stderr, "Cannot initialize the conversion context\n");
tomwalters@116 335 exit(1);
tomwalters@116 336 }
tomwalters@116 337 }
tomwalters@116 338 fill_image(tmp_picture, pFrameBuffer, c->width, c->height);
tomwalters@116 339 sws_scale(img_convert_ctx, tmp_picture->data, tmp_picture->linesize,
tomwalters@116 340 0, c->height, picture->data, picture->linesize);
tomwalters@116 341 } else {
tomwalters@116 342 fill_image(picture, pFrameBuffer, c->width, c->height);
tomwalters@116 343 }
tomwalters@116 344 /* encode the image */
tomwalters@116 345 out_size = avcodec_encode_video(c, video_outbuf, video_outbuf_size, picture);
tomwalters@116 346 /* if zero size, it means the image was buffered */
tomwalters@116 347 if (out_size > 0) {
tomwalters@116 348 AVPacket pkt;
tomwalters@116 349 av_init_packet(&pkt);
tomwalters@116 350
tomwalters@116 351 pkt.pts= av_rescale_q(c->coded_frame->pts, c->time_base, video_st->time_base);
tomwalters@116 352 if(c->coded_frame->key_frame) {
tomwalters@116 353 pkt.flags |= PKT_FLAG_KEY;
tomwalters@116 354 pkt.stream_index= video_st->index;
tomwalters@116 355 pkt.data= video_outbuf;
tomwalters@116 356 pkt.size= out_size;
tomwalters@116 357 /* write the compressed frame in the media file */
tomwalters@116 358 ret = av_write_frame(oc, &pkt);
tomwalters@116 359 }
tomwalters@116 360 } else {
tomwalters@116 361 ret = 0;
tomwalters@116 362 }
tomwalters@116 363 if (ret != 0) {
tomwalters@116 364 fprintf(stderr, "Error while writing video frame\n");
tomwalters@116 365 exit(1);
tomwalters@116 366 }
tomwalters@116 367 }
tomwalters@116 368
tomwalters@116 369 void LibavformatWriter::End() {
tomwalters@116 370 /* close each codec */
tomwalters@116 371 if (video_st) {
tomwalters@116 372 close_video(oc, video_st);
tomwalters@116 373 }
tomwalters@116 374 /* write the trailer, if any */
tomwalters@116 375 av_write_trailer(oc);
tomwalters@116 376
tomwalters@116 377 /* free the streams */
tomwalters@116 378 for(i = 0; i < oc->nb_streams; i++) {
tomwalters@116 379 av_freep(&oc->streams[i]->codec);
tomwalters@116 380 av_freep(&oc->streams[i]);
tomwalters@116 381 }
tomwalters@116 382
tomwalters@116 383 if (!(fmt->flags & AVFMT_NOFILE)) {
tomwalters@116 384 /* close the output file */
tomwalters@116 385 url_fclose(&oc->pb);
tomwalters@116 386 }
tomwalters@116 387 /* free the stream */
tomwalters@116 388 av_free(oc);
tomwalters@116 389 }
tomwalters@116 390
tomwalters@116 391 void LibavformatWriter::fill_image(AVFrame *pict,
tomwalters@116 392 unsigned char *pFrameBuffer,
tomwalters@116 393 int width,
tomwalters@116 394 int height) {
tomwalters@117 395 memcpy((void*)&(pict->data[0][0]), (void*)pFrameBuffer, width*height*4);
tomwalters@116 396 }