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

Last change on this file since 3452 was 3337, checked in by gah, 11 years ago

fixes for new version of ffmeg

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