source: trunk/packages/vizservers/nanovis/RpAVTranslate.cpp @ 2810

Last change on this file since 2810 was 2810, checked in by ldelgass, 12 years ago

Movie bitrate flag is an int not a float

  • Property svn:eol-style set to native
File size: 12.4 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * ======================================================================
4 *  Rappture::AVTranslate
5 *
6 *  AUTHOR:  Derrick Kearney, Purdue University
7 *
8 *  Copyright (c) 2005-2009  Purdue Research Foundation
9 * ----------------------------------------------------------------------
10 *  See the file "license.terms" for information on usage and
11 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 * ======================================================================
13 */
14
15
16#include "nvconf.h"
17
18#if defined(HAVE_LIBAVCODEC) || defined(HAVE_LIBAVFORMAT)
19#include <cstdlib>
20#include <cstdio>
21#include <cstring>
22#include <cmath>
23#include <fstream>
24#include <assert.h>
25
26extern "C" {
27#ifdef HAVE_FFMPEG_MEM_H
28#include <ffmpeg/mem.h>
29#endif
30#ifdef HAVE_LIBAVUTIL_MEM_H
31#include <libavutil/mem.h>
32#endif
33}
34
35#include "RpAVTranslate.h"
36
37#ifndef HAVE_AVMEDIA_TYPE_VIDEO
38#define AVMEDIA_TYPE_VIDEO      CODEC_TYPE_VIDEO
39#endif  /* HAVE_AVMEDIA_TYPE_VIDEO */
40
41#ifndef AV_PKT_FLAG_KEY
42#define AV_PKT_FLAG_KEY         PKT_FLAG_KEY           
43#endif
44
45#ifndef HAVE_AV_GUESS_FORMAT
46#define av_guess_format         guess_format
47#endif  /*HAVE_AV_GUESS_FORMAT*/
48
49#ifndef HAVE_AV_DUMP_FORMAT
50#define av_dump_format          dump_format
51#endif  /*HAVE_AV_DUMP_FORMAT*/
52
53#ifndef HAVE_AVIO_OPEN
54#define avio_open               url_fopen       
55#endif  /*HAVE_AVIO_OPEN*/
56
57#ifndef HAVE_AVIO_CLOSE
58#define avio_close              url_fclose     
59#endif  /*HAVE_AVIO_CLOSE*/
60
61#ifndef M_PI
62#define M_PI 3.14159265358979323846
63#endif
64
65using namespace Rappture;
66
67AVTranslate::AVTranslate(size_t width, size_t height) :
68    _width (width),
69    _height (height),
70    _bitRate(400000),
71    _frameRate(25.0f),
72    _videoOutbufSize(200000),
73    _videoOutbuf(NULL),
74    _fmtPtr(NULL),
75    _ocPtr(NULL),
76    _avStreamPtr(NULL),
77    _pictPtr(NULL),
78    _rgbPictPtr(NULL)
79{
80}
81
82AVTranslate::AVTranslate(size_t width, size_t height, size_t bitRate,
83                         float frameRate) :
84    _width (width),
85    _height (height),
86    _bitRate(bitRate),
87    _frameRate(frameRate),
88    _videoOutbufSize(200000),
89    _videoOutbuf(NULL),
90    _fmtPtr(NULL),
91    _ocPtr(NULL),
92    _avStreamPtr(NULL),
93    _pictPtr(NULL),
94    _rgbPictPtr(NULL)
95{
96}
97
98/**
99 * Copy constructor
100 * @param AVTranslate object to copy
101 */
102 /*
103AVTranslate::AVTranslate(const AVTranslate& o)
104{}
105*/
106
107AVTranslate::~AVTranslate()
108{
109#ifdef notdef
110    /* FIXME:: This can't be right.  Don't want to automatically write out
111     * trailer etc. on destruction of this object. */
112    done();
113#endif
114}
115
116
117bool
118AVTranslate::init(Outcome &status, const char *filename)
119{
120    status.addContext("Rappture::AVTranslate::init()");
121    /* Initialize libavcodec, and register all codecs and formats */
122    avcodec_init();
123    avcodec_register_all();
124    av_register_all();
125
126    /* Auto detect the output format from the name. default is mpeg. */
127    _fmtPtr = av_guess_format(NULL, filename, NULL);
128    if (_fmtPtr == NULL) {
129        /*
130          TRACE(  "Could not deduce output format from"
131                 "file extension: using MPEG.\n");
132        */
133        _fmtPtr = av_guess_format("mpeg", NULL, NULL);
134    }
135    if (_fmtPtr == NULL) {
136        status.addError("Could not find suitable output format");
137        return false;
138    }
139
140#ifdef HAVE_AVFORMAT_ALLOC_CONTEXT
141    /* Allocate the output media context. */
142    _ocPtr = avformat_alloc_context();
143#else
144    _ocPtr = av_alloc_format_context();
145#endif
146    if (!_ocPtr) {
147        status.addError("Memory error while allocating format context");
148        return false;
149    }
150    _ocPtr->oformat = _fmtPtr;
151    snprintf(_ocPtr->filename, sizeof(_ocPtr->filename), "%s", filename);
152
153    /* Add the video stream using the default format codecs and initialize the
154       codecs. */
155    _avStreamPtr = NULL;
156    if (_fmtPtr->video_codec != CODEC_ID_NONE) {
157        if ( (!addVideoStream(status, _fmtPtr->video_codec, &_avStreamPtr)) ) {
158            return false;
159        }
160    }
161
162#if defined(HAVE_AV_SET_PARAMETERS) && !defined(HAVE_AVFORMAT_WRITE_HEADER)
163    /* Set the output parameters (must be done even if no parameters). */
164    if (av_set_parameters(_ocPtr, NULL) < 0) {
165        status.addError("Invalid output format parameters");
166        return false;
167    }
168#endif
169    av_dump_format(_ocPtr, 0, filename, 1);
170
171    /* Now that all the parameters are set, we can open the video codec and
172       allocate the necessary encode buffers */
173    if (_avStreamPtr) {
174        if (!openVideo(status)) {
175            return false;
176        }
177    }
178
179    /* Open the output file, if needed. */
180    if (!(_fmtPtr->flags & AVFMT_NOFILE)) {
181        if (avio_open(&_ocPtr->pb, filename, URL_WRONLY) < 0) {
182            status.addError("Could not open '%s'", filename);
183            return false;
184        }
185    }
186
187    /* write the stream header, if any */
188#ifdef HAVE_AVFORMAT_WRITE_HEADER
189    avformat_write_header(_ocPtr, NULL);
190#else
191    av_write_header(_ocPtr);
192#endif
193    return true;
194}
195
196bool
197AVTranslate::append(Outcome &status, uint8_t *rgbData, size_t linePad)
198{
199    status.addContext("Rappture::AVTranslate::append()");
200    if (rgbData == NULL) {
201        status.addError("rdbData pointer is NULL");
202        return false;
203    }
204    /* Copy the data into the picture without the padding and reversing the
205     * rows. Note that the origin of the GL image is the lower-left while for
206     * the movie it's upper-left. */
207    size_t bytesPerRow = _width * 3;
208    size_t bytesPerLine = bytesPerRow + linePad;
209    uint8_t *srcRowPtr = rgbData + ((_height - 1) * bytesPerLine);
210    uint8_t *destPtr = _rgbPictPtr->data[0];
211    for (size_t y = 0; y < _height; y++) {
212        uint8_t *sp, *send;
213       
214        for (sp = srcRowPtr, send = sp + bytesPerRow; sp < send; sp++, destPtr++) {
215            *destPtr = *sp;
216        }
217        srcRowPtr -= bytesPerLine;
218    }
219
220#ifdef HAVE_IMG_CONVERT
221    // Use img_convert instead of sws_scale because img_convert is LGPL and
222    // sws_scale is GPL
223    img_convert((AVPicture *)_pictPtr, PIX_FMT_YUV420P,
224                (AVPicture *)_rgbPictPtr, PIX_FMT_RGB24,
225                _width, _height);
226#endif  /*HAVE_IMG_CONVERT*/
227    writeVideoFrame(status);
228
229    return true;
230}
231
232
233bool
234AVTranslate::done(Outcome &status)
235{
236    size_t i = 0;
237
238    /* Close each codec */
239    if (_avStreamPtr) {
240        closeVideo(status);
241    }
242
243    /* Write the trailer, if any */
244    av_write_trailer(_ocPtr);
245
246    /* Free the streams */
247    for(i = 0; i < _ocPtr->nb_streams; i++) {
248        av_freep(&_ocPtr->streams[i]->codec);
249        // _ocPtr->streams[i]->codec = NULL;
250
251        av_freep(&_ocPtr->streams[i]);
252        // _ocPtr->streams[i] = NULL;
253    }
254
255    if (!(_fmtPtr->flags & AVFMT_NOFILE)) {
256        /* close the output file */
257        avio_close(_ocPtr->pb);
258    }
259
260    /* Free the stream */
261    av_free(_ocPtr);
262    _ocPtr = NULL;
263    return true;
264}
265
266
267/* Add a video output stream */
268bool
269AVTranslate::addVideoStream(Outcome &status, CodecID codec_id,
270                            AVStream **streamPtrPtr)
271{
272    status.addContext("Rappture::AVTranslate::add_video_stream()");
273    if (streamPtrPtr == NULL) {
274        status.addError("AVStream **st is NULL");
275        return false;
276    }
277
278    AVStream *streamPtr;
279    streamPtr = av_new_stream(_ocPtr, 0);
280    if (streamPtr == NULL) {
281        status.addError("Could not alloc stream");
282        return false;
283    }
284
285    AVCodecContext *codecPtr;
286    codecPtr = streamPtr->codec;
287    codecPtr->codec_id = codec_id;
288    codecPtr->codec_type = AVMEDIA_TYPE_VIDEO;
289
290    /* Put sample parameters */
291    codecPtr->bit_rate = _bitRate;
292    /* resolution must be a multiple of two */
293    codecPtr->width = _width;
294    codecPtr->height = _height;
295    /* time base: this is the fundamental unit of time (in seconds) in terms
296       of which frame timestamps are represented. for fixed-fps content,
297       timebase should be 1/framerate and timestamp increments should be
298       identically 1. */
299    codecPtr->time_base.den = _frameRate;
300    codecPtr->time_base.num = 1;
301    codecPtr->gop_size = 12;            /* Emit one intra frame every twelve
302                                         * frames at most */
303    codecPtr->pix_fmt = PIX_FMT_YUV420P;
304    if (codecPtr->codec_id == CODEC_ID_MPEG2VIDEO) {
305        /* just for testing, we also add B frames */
306        codecPtr->max_b_frames = 2;
307    }
308    if (codecPtr->codec_id == CODEC_ID_MPEG1VIDEO) {
309        /* Needed to avoid using macroblocks in which some coeffs overflow.
310           This does not happen with normal video, it just happens here as the
311           motion of the chroma plane does not match the luma plane. */
312        codecPtr->mb_decision=2;
313    }
314    /* some formats want stream headers to be separate */
315    if((strcmp(_ocPtr->oformat->name, "mp4") == 0) ||
316       (strcmp(_ocPtr->oformat->name, "mov") == 0) ||
317       (strcmp(_ocPtr->oformat->name, "3gp") == 0)) {
318        codecPtr->flags |= CODEC_FLAG_GLOBAL_HEADER;
319    }
320    *streamPtrPtr = streamPtr;
321    return true;
322}
323
324bool
325AVTranslate::allocPicture(Outcome &status, PixelFormat pixFmt,
326     AVFrame **framePtrPtr)
327{
328    status.addContext("Rappture::AVTranslate::allocPicture()");
329    if (framePtrPtr == NULL) {
330        status.addError("AVFrame **p == NULL");
331        return false;
332    }
333
334    AVFrame *framePtr;
335    framePtr = avcodec_alloc_frame();
336    if (framePtr == NULL) {
337        status.addError("Memory error: Could not alloc frame");
338        return false;
339    }
340
341    size_t size;
342    size = avpicture_get_size(pixFmt, _width, _height);
343
344    uint8_t *bits;
345    bits = (uint8_t *)av_malloc(size);
346    if (bits == NULL) {
347        av_free(framePtr);
348        status.addError("Memory error: Could not alloc picture buffer");
349        return false;
350    }
351    avpicture_fill((AVPicture *)framePtr, bits, pixFmt, _width, _height);
352    *framePtrPtr = framePtr;
353    return true;
354}
355
356bool
357AVTranslate::openVideo(Outcome &status)
358{
359    AVCodec *codec;
360    AVCodecContext *c;
361
362    status.addContext("Rappture::AVTranslate::openVideo()");
363    c = _avStreamPtr->codec;
364
365    /* find the video encoder */
366    codec = avcodec_find_encoder(c->codec_id);
367    if (codec == NULL) {
368        status.addError("can't find codec %d\n", c->codec->id);
369        return false;
370    }
371
372    /* open the codec */
373    if (avcodec_open(c, codec) < 0) {
374        status.addError("can't open codec %d", c->codec->id);
375        return false;
376    }
377
378    _videoOutbuf = NULL;
379    if (!(_ocPtr->oformat->flags & AVFMT_RAWPICTURE)) {
380        /* allocate output buffer */
381        /* XXX: API change will be done */
382        /* buffers passed into lav* can be allocated any way you prefer,
383           as long as they're aligned enough for the architecture, and
384           they're freed appropriately (such as using av_free for buffers
385           allocated with av_malloc) */
386        _videoOutbuf = (uint8_t *) av_malloc(_videoOutbufSize);
387    }
388    /* Allocate the encoded raw picture */
389    if (!allocPicture(status, c->pix_fmt, &_pictPtr)) {
390        return false;
391    }
392    if (!allocPicture(status, PIX_FMT_RGB24, &_rgbPictPtr)) {
393        status.addError("allocPicture: can't allocate picture");
394        return false;
395    }
396    return true;
397}
398
399bool
400AVTranslate::writeVideoFrame(Outcome &status)
401{
402    AVCodecContext *codecPtr;
403
404    status.addContext("Rappture::AVTranslate::writeVideoframe()");
405    codecPtr = _avStreamPtr->codec;
406
407    /* encode the image */
408    int size;
409    size = avcodec_encode_video(codecPtr, _videoOutbuf, _videoOutbufSize,
410        _pictPtr);
411    if (size < 0) {
412        status.addError("Error while writing video frame");
413        return false;
414    }
415    if (size == 0) {
416        return true;            /* Image was buffered */
417    }
418    AVPacket pkt;
419    av_init_packet(&pkt);
420
421    pkt.pts = av_rescale_q(codecPtr->coded_frame->pts, codecPtr->time_base,
422                           _avStreamPtr->time_base);
423    if (codecPtr->coded_frame->key_frame) {
424        pkt.flags |= AV_PKT_FLAG_KEY;
425    }
426    pkt.stream_index = _avStreamPtr->index;
427    pkt.data = _videoOutbuf;
428    pkt.size = size;
429   
430    /* write the compressed frame i the media file */
431    if (av_write_frame(_ocPtr, &pkt) < 0) {
432        status.addError("Error while writing video frame");
433        return false;
434    }
435    return true;
436}
437
438
439bool
440AVTranslate::closeVideo(Outcome &status)
441{
442    if (_avStreamPtr != NULL) {
443        avcodec_close(_avStreamPtr->codec);
444    }
445    if (_pictPtr != NULL) {
446        av_free(_pictPtr->data[0]);
447        av_free(_pictPtr);
448        _pictPtr = NULL;
449    }
450    if (_rgbPictPtr != NULL) {
451        av_free(_rgbPictPtr->data[0]);
452        av_free(_rgbPictPtr);
453        _rgbPictPtr = NULL;
454    }
455    if (_videoOutbuf != NULL) {
456        av_free(_videoOutbuf);
457        _videoOutbuf = NULL;
458    }
459    return true;
460}
461
462/*
463status.addError("error while opening file");
464status.addContext("Rappture::Buffer::dump()");
465return status;
466*/
467
468#endif /* HAVE_LIBAVCODEC && HAVE_LIBAVFORMAT */
Note: See TracBrowser for help on using the repository browser.