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

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