summaryrefslogtreecommitdiff
path: root/rotord/libavwrapper_guarded.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'rotord/libavwrapper_guarded.cpp')
-rwxr-xr-xrotord/libavwrapper_guarded.cpp1614
1 files changed, 1614 insertions, 0 deletions
diff --git a/rotord/libavwrapper_guarded.cpp b/rotord/libavwrapper_guarded.cpp
new file mode 100755
index 0000000..8e40145
--- /dev/null
+++ b/rotord/libavwrapper_guarded.cpp
@@ -0,0 +1,1614 @@
+#include "libavwrapper_guarded.h"
+
+extern Poco::Mutex mutex; //application wide mutex
+static Poco::Mutex mutex;
+
+///this seems to create a long pause and finally crash
+
+extern "C"
+{
+#include <libswscale/swscale.h>
+}
+
+/*
+#include <QNetworkReply>
+#include <QNetworkRequest>
+#include <QEventLoop>
+#include <QFileInfo>
+#include <QMutexLocker>
+#include <QDebug>
+*/
+
+#include <stdexcept>
+#include <iostream>
+#include <cassert>
+
+using namespace std;
+
+// Translated to C++ by Christopher Bruns May 2012
+// from ffmeg_adapt.c in whisk package by Nathan Clack, Mark Bolstadt, Michael Meeuwisse
+
+//QMutex decoder::mutex;
+
+// Avoid link error on some macs
+#ifdef __APPLE__
+extern "C" {
+#include <stdlib.h>
+#include <errno.h>
+// #include "compiler/compiler.h"
+
+/*
+ * Darwin doesn't have posix_memalign(), provide a private
+ * weak alternative
+ */
+ /*
+int __weak posix_memalign(void **ptr, size_t align, size_t size)
+{
+ if (*ptr)
+ return 0;
+
+ return ENOMEM;
+}
+*/
+}
+#endif
+
+// Custom read function so FFMPEG does not need to read from a local file by name.
+// But rather from a stream derived from a URL or whatever.
+extern "C" {
+
+int readFunction(void* opaque, uint8_t* buf, int buf_size)
+{
+ //QIODevice* stream = (QIODevice*)opaque;
+ ifstream* stream = (ifstream*)opaque;
+ //int numBytes =
+ stream->read((char*)buf, (streamsize)buf_size);
+ return stream->gcount(); //?? is this right
+ //numBytes; //TODO work out
+}
+
+// http://cdry.wordpress.com/2009/09/09/using-custom-io-callbacks-with-ffmpeg/
+int64_t seekFunction(void* opaque, int64_t offset, int whence)
+{
+ //QIODevice* stream = (QIODevice*)opaque;
+ ifstream* stream = (ifstream*)opaque;
+ if (stream == NULL)
+ return -1;
+ else if (whence == AVSEEK_SIZE)
+ return -1; // "size of my handle in bytes"
+ //else if (stream->isSequential())
+ // return -1; // cannot seek a sequential stream //presume this would be certain kind of network stream
+ else if (whence == SEEK_CUR) { // relative to start of file
+ if (! stream->seekg(offset,ios_base::cur)) //stream->pos() + offset) )
+ return -1;
+ }
+ else if (whence == SEEK_END) { // relative to end of file
+ assert(offset < 0);
+ if (! stream->seekg(offset,ios_base::end)) //stream->size() + offset) )
+ return -1;
+ }
+ else if (whence == SEEK_SET) { // relative to start of file
+ if (! stream->seekg(offset) )
+ return -1;
+ }
+ else {
+ assert(false);
+ }
+ return stream->tellg();
+}
+
+}
+
+
+/////////////////////////////
+// AVPacketWrapper methods //
+/////////////////////////////
+
+
+class AVPacketWrapper
+{
+public:
+ AVPacketWrapper();
+ virtual ~AVPacketWrapper();
+ void free();
+
+ AVPacket packet;
+};
+
+
+AVPacketWrapper::AVPacketWrapper()
+{
+ packet.destruct = NULL;
+}
+
+/* virtual */
+AVPacketWrapper::~AVPacketWrapper()
+{
+ free();
+}
+
+void AVPacketWrapper::free()
+{
+ av_free_packet(&packet);
+}
+
+
+//bool libav::b_is_one_time_inited = false;
+
+/////////////////////////
+// decoder methods //
+/////////////////////////
+
+libav::decoder::decoder(PixelFormat pixelFormat)
+ : isOpen(false)
+{
+ mutex.lock();
+ initialize();
+ format = pixelFormat;
+ mutex.unlock();
+}
+
+/*
+decoder::decoder(QUrl url, PixelFormat pixelFormat)
+ : isOpen(false)
+{
+ //QMutexLocker lock(&decoder::mutex);
+ initialize();
+ format = pixelFormat;
+ isOpen = open(url, pixelFormat);
+}
+*/
+
+/* virtual */
+libav::decoder::~decoder()
+{
+ //QMutexLocker lock(&decoder::mutex);
+ mutex.lock();
+ if (NULL != Sctx) {
+ sws_freeContext(Sctx);
+ Sctx = NULL;
+ }
+ if (NULL != pRaw) {
+ av_free(pRaw);
+ pRaw = NULL;
+ }
+ if (NULL != pFrameRGB) {
+ av_free(pFrameRGB);
+ pFrameRGB = NULL;
+ }
+ if (NULL != pCtx) {
+ avcodec_close(pCtx);
+ pCtx = NULL;
+ }
+ if (NULL != container) {
+ avformat_close_input(&container);
+ container = NULL;
+ }
+ if (NULL != buffer) {
+ av_free(buffer);
+ buffer = NULL;
+ }
+ if (NULL != blank) {
+ av_free(blank);
+ blank = NULL;
+ }
+ mutex.unlock();
+ /*
+ if (NULL != avioContext) {
+ av_free(avioContext);
+ avioContext = NULL;
+ }
+ */
+ //QNetworkreply
+ //if (reply != NULL) {
+ // reply->deleteLater();
+ // reply = NULL;
+ //}
+ // Don't need to free pCodec?
+}
+/*
+bool decoder::open(QUrl url, enum PixelFormat formatParam)
+{
+ if (url.isEmpty())
+ return false;
+
+ // Is the movie source a local file?
+ if (url.host() == "localhost")
+ url.setHost("");
+ QString fileName = url.toLocalFile();
+ if ( (! fileName.isEmpty())
+ && (QFileInfo(fileName).exists()) )
+ {
+ // return open(fileName, formatParam); // for testing only
+
+ // Yes, the source is a local file
+ fileStream.setFileName(fileName);
+ // qDebug() << fileName;
+ if (! fileStream.open(QIODevice::ReadOnly))
+ return false;
+ return open(fileStream, fileName, formatParam);
+ }
+
+ // ...No, the source is not a local file
+ if (url.host() == "")
+ url.setHost("localhost");
+ fileName = url.path();
+
+ // http://stackoverflow.com/questions/9604633/reading-a-file-located-in-memory-with-libavformat
+ // Load from URL
+ QEventLoop loop; // for synchronous url fetch http://stackoverflow.com/questions/5486090/qnetworkreply-wait-for-finished
+ QObject::connect(&networkManager, SIGNAL(finished(QNetworkReply*)),
+ &loop, SLOT(quit()));
+ QNetworkRequest request = QNetworkRequest(url);
+ // qDebug() << "networkManager" << __FILE__ << __LINE__;
+ reply = networkManager.get(request);
+ loop.exec();
+ if (reply->error() != QNetworkReply::NoError) {
+ // qDebug() << reply->error();
+ reply->deleteLater();
+ reply = NULL;
+ return false;
+ }
+ QIODevice * stream = reply;
+ // Mpeg needs seekable device, so create in-memory buffer if necessary
+ if (stream->isSequential()) {
+ byteArray = stream->readAll();
+ fileBuffer.setBuffer(&byteArray);
+ fileBuffer.open(QIODevice::ReadOnly);
+ if (! fileBuffer.seek(0))
+ return false;
+ stream = &fileBuffer;
+ assert(! stream->isSequential());
+ }
+ bool result = open(*stream, fileName, formatParam);
+ return result;
+}
+
+bool decoder::open(QIODevice& fileStream, QString& fileName, enum PixelFormat formatParam)
+{
+ // http://stackoverflow.com/questions/9604633/reading-a-file-located-in-memory-with-libavformat
+ // I think AVIOContext is the trick used to redivert the input stream
+ ioBuffer = (unsigned char *)av_malloc(ioBufferSize + FF_INPUT_BUFFER_PADDING_SIZE); // can get av_free()ed by libav
+ avioContext = avio_alloc_context(ioBuffer, ioBufferSize, 0, (void*)(&fileStream), &readFunction, NULL, &seekFunction);
+ container = avformat_alloc_context();
+ container->pb = avioContext;
+
+ // Open file, check usability
+ std::string fileNameStd = fileName.toStdString();
+ if (!avtry( avformat_open_input(&container, fileNameStd.c_str(), NULL, NULL), fileNameStd ))
+ return false;
+ return openUsingInitializedContainer(formatParam);
+}
+*/
+// file name based method for historical continuity
+bool libav::decoder::open(char* fileName, enum PixelFormat formatParam){
+
+ if (!avtry( avformat_open_input(&container, fileName, NULL, NULL), string(fileName) ))
+ return false;
+ return openUsingInitializedContainer(formatParam);
+}
+bool libav::decoder::open(string& fileName, enum PixelFormat formatParam)
+{
+ // Open file, check usability
+
+ if (!avtry( avformat_open_input(&container, fileName.c_str(), NULL, NULL), fileName ))
+ return false;
+ return openUsingInitializedContainer(formatParam);
+}
+
+
+bool libav::decoder::openUsingInitializedContainer(enum PixelFormat formatParam)
+{
+ format = formatParam;
+ sc = getNumberOfChannels();
+
+ if (!avtry( avformat_find_stream_info(container, NULL), "Cannot find stream information." ))
+ return false;
+ if (!avtry( videoStream=av_find_best_stream(container, AVMEDIA_TYPE_VIDEO, -1, -1, &pCodec, 0), "Cannot find a video stream." ))
+ return false;
+ pCtx=container->streams[videoStream]->codec;
+ width = pCtx->width;
+ height = pCtx->height;
+ if (!avtry( avcodec_open2(pCtx, pCodec, NULL), "Cannot open video decoder." ))
+ return false;
+
+ /* Frame rate fix for some codecs */
+ if( pCtx->time_base.num > 1000 && pCtx->time_base.den == 1 )
+ pCtx->time_base.den = 1000;
+
+ /* Compute the total number of frames in the file */
+ /* duration is in microsecs */
+ numFrames = (int)(( container->duration / (double)AV_TIME_BASE ) * pCtx->time_base.den + 0.5);
+
+ /* Get framebuffers */
+ if (! (pRaw = avcodec_alloc_frame()) )
+ throw std::runtime_error("");
+ if (! (pFrameRGB = avcodec_alloc_frame()) )
+ throw std::runtime_error("");
+
+ /* Create data buffer */
+ if (format == PIX_FMT_NONE) {
+ numBytes = 0;
+ buffer = NULL;
+ blank = NULL;
+ pFrameRGB = NULL;
+ Sctx = NULL;
+ }
+ else {
+ numBytes = avpicture_get_size( format, pCtx->width, pCtx->height ); // RGB24 format
+ if (! (buffer = (uint8_t*)av_malloc(numBytes + FF_INPUT_BUFFER_PADDING_SIZE)) ) // RGB24 format
+ throw std::runtime_error("");
+ if (! (blank = (uint8_t*)av_mallocz(avpicture_get_size(pCtx->pix_fmt,width,height))) ) // native codec format
+ throw std::runtime_error("");
+
+ /* Init buffers */
+ avpicture_fill( (AVPicture * ) pFrameRGB, buffer, format,
+ pCtx->width, pCtx->height );
+
+ /* Init scale & convert */
+ if (! (Sctx=sws_getContext(
+ pCtx->width,
+ pCtx->height,
+ pCtx->pix_fmt,
+ width,
+ height,
+ format,
+ SWS_POINT, // fastest?
+ NULL,NULL,NULL)) )
+ throw std::runtime_error("");
+ }
+
+ /* Give some info on stderr about the file & stream */
+ //dump_format(container, 0, fname, 0);
+
+ previousFrameIndex = -1;
+ return true;
+}
+
+bool libav::decoder::fetchFrame(int targetFrameIndex)
+{
+ if ((targetFrameIndex < 0) || (targetFrameIndex > numFrames))
+ return false;
+ if (targetFrameIndex == (previousFrameIndex + 1)) {
+ if (! readNextFrame(targetFrameIndex))
+ return false;
+ }
+ else {
+ int64_t response=seekToFrame(targetFrameIndex);
+ if (response < 0)
+ return false;
+ if (response!=targetFrameIndex){
+ cerr<<"libav::decoder asked for "<<targetFrameIndex<<", got "<<response<<endl; //does not seem to be aware of wrong frame
+ }
+ }
+ previousFrameIndex = targetFrameIndex;
+ return true;
+}
+
+// \returns current frame on success, otherwise -1
+int libav::decoder::seekToFrame(int targetFrameIndex)
+{
+ int64_t duration = container->streams[videoStream]->duration;
+ int64_t ts = av_rescale(duration,targetFrameIndex,numFrames);
+ int64_t tol = av_rescale(duration,1,2*numFrames);
+ if ( (targetFrameIndex < 0) || (targetFrameIndex >= numFrames) ) {
+ return -1;
+ }
+ int result = avformat_seek_file( container, //format context
+ videoStream,//stream id
+ 0, //min timestamp 0?
+ ts, //target timestamp
+ ts, //max timestamp
+ 0);//flags AVSEEK_FLAG_ANY //doesn't seem to work great
+ if (result < 0)
+ return -1;
+
+ avcodec_flush_buffers(pCtx);
+ if (! readNextFrame(targetFrameIndex))
+ return -1;
+
+ return targetFrameIndex;
+}
+
+bool libav::decoder::readNextFrame(int targetFrameIndex)
+{
+ AVPacket packet = {0};
+ av_init_packet(&packet);
+ bool result = readNextFrameWithPacket(targetFrameIndex, packet, pRaw);
+ av_free_packet(&packet);
+ return result;
+}
+
+// WARNING this method can raise an exception
+bool libav::decoder::readNextFrameWithPacket(int targetFrameIndex, AVPacket& packet, AVFrame* pYuv)
+{
+ int finished = 0;
+ do {
+ finished = 0;
+ av_free_packet(&packet);
+ int result;
+ if (!avtry(av_read_frame( container, &packet ), "Failed to read frame"))
+ return false; // !!NOTE: see docs on packet.convergence_duration for proper seeking
+ if( packet.stream_index != videoStream ) /* Is it what we're trying to parse? */
+ continue;
+ if (!avtry(avcodec_decode_video2( pCtx, pYuv, &finished, &packet ), "Failed to decode video"))
+ return false;
+ // handle odd cases and debug
+ if((pCtx->codec_id==CODEC_ID_RAWVIDEO) && !finished)
+ {
+ avpicture_fill( (AVPicture * ) pYuv, blank, pCtx->pix_fmt,width, height ); // set to blank frame
+ finished = 1;
+ }
+#if 0 // very useful for debugging
+ cout << "Packet - pts:" << (int)packet.pts;
+ cout << " dts:" << (int)packet.dts;
+ cout << " - flag: " << packet.flags;
+ cout << " - finished: " << finished;
+ cout << " - Frame pts:" << (int)pYuv->pts;
+ cout << " " << (int)pYuv->best_effort_timestamp;
+ cout << endl;
+ /* printf("Packet - pts:%5d dts:%5d (%5d) - flag: %1d - finished: %3d - Frame pts:%5d %5d\n",
+ (int)packet.pts,(int)packet.dts,
+ packet.flags,finished,
+ (int)pYuv->pts,(int)pYuv->best_effort_timestamp); */
+#endif
+ if(!finished) {
+ if (packet.pts == AV_NOPTS_VALUE)
+ throw std::runtime_error("");
+ if (packet.size == 0) // packet.size==0 usually means EOF
+ break;
+ }
+ } while ( (!finished) || (pYuv->best_effort_timestamp < targetFrameIndex));
+
+ av_free_packet(&packet);
+
+ if (format != PIX_FMT_NONE) {
+ sws_scale(Sctx, // sws context
+ pYuv->data, // src slice
+ pYuv->linesize, // src stride
+ 0, // src slice origin y
+ pCtx->height, // src slice height
+ pFrameRGB->data, // dst
+ pFrameRGB->linesize ); // dst stride
+ }
+
+ previousFrameIndex = targetFrameIndex;
+ return true;
+}
+
+uint8_t libav::decoder::getPixelIntensity(int x, int y, Channel c) const
+{
+ return *(pFrameRGB->data[0] + y * pFrameRGB->linesize[0] + x * sc + c);
+}
+
+int libav::decoder::getNumberOfFrames() const { return numFrames; }
+
+int libav::decoder::getWidth() const { return width; }
+
+int libav::decoder::getHeight() const { return height; }
+
+int libav::decoder::getNumberOfChannels() const
+{
+ switch(format)
+ {
+ case PIX_FMT_BGRA:
+ return 4;
+ break;
+ case PIX_FMT_RGB24:
+ return 3;
+ break;
+ case PIX_FMT_GRAY8:
+ return 1;
+ break;
+ default:
+ return 0;
+ break;
+ }
+ return 0;
+}
+
+void libav::decoder::initialize()
+{
+ Sctx = NULL;
+ pRaw = NULL;
+ pFrameRGB = NULL;
+ pCtx = NULL;
+ container = NULL;
+ buffer = NULL;
+ blank = NULL;
+ pCodec = NULL;
+ format = PIX_FMT_NONE;
+ //network stuff
+ //reply = NULL;
+ //ioBuffer = NULL;
+ //avioContext = NULL;
+ maybeInitFFMpegLib();
+}
+
+void libav::maybeInitFFMpegLib()
+{
+ if (b_is_one_time_inited)
+ return;
+ av_register_all();
+ avcodec_register_all();
+ avformat_network_init();
+ b_is_one_time_inited = true;
+}
+
+bool libav::decoder::avtry(int result, const std::string& msg) {
+ if ((result < 0) && (result != AVERROR_EOF)) {
+ char buf[1024];
+ av_strerror(result, buf, sizeof(buf));
+ std::string message = std::string("libav::Error: ") + msg + buf;
+ //qDebug() << QString(message.c_str());
+ cerr<<message<<endl;
+ return false;
+ }
+ return true;
+}
+
+
+
+
+///////////////////////////
+// encoder methods //
+///////////////////////////
+
+
+libav::encoder::encoder(const char * file_name, int width, int height, float _framerate,enum AVCodecID codec_id)
+ : picture_yuv(NULL)
+ , picture_rgb(NULL)
+ , container(NULL)
+{
+ //multiply float seconds by this to get pts
+ timebase=((float)AV_TIME_BASE_Q.den)/(AV_TIME_BASE_Q.num*_framerate*3.125f); //no idea where the 3.125 comes from
+
+ if (0 != (width % 2))
+ cerr << "WARNING: Video width is not a multiple of 2" << endl;
+ if (0 != (height % 2))
+ cerr << "WARNING: Video height is not a multiple of 2" << endl;
+
+ maybeInitFFMpegLib();
+
+ container = avformat_alloc_context();
+ if (NULL == container)
+ throw std::runtime_error("Unable to allocate format context");
+
+ AVOutputFormat * fmt = av_guess_format(NULL, file_name, NULL);
+ if (!fmt)
+ fmt = av_guess_format("mpeg", NULL, NULL);
+ if (!fmt)
+ throw std::runtime_error("Unable to deduce video format");
+ container->oformat = fmt;
+
+ fmt->video_codec = codec_id;
+ // fmt->video_codec = CODEC_ID_H264; // fails to write
+
+ AVStream * video_st = avformat_new_stream(container, NULL);
+
+ pCtx = video_st->codec;
+ pCtx->codec_id = fmt->video_codec;
+ pCtx->codec_type = AVMEDIA_TYPE_VIDEO;
+ // resolution must be a multiple of two
+ pCtx->width = width;
+ pCtx->height = height;
+
+ // bit_rate determines image quality
+ pCtx->bit_rate = width * height * 4; // ?
+ // pCtx->qmax = 50; // no effect?
+
+ // "high quality" parameters from http://www.cs.ait.ac.th/~on/mplayer/pl/menc-feat-enc-libavcodec.html
+ // vcodec=mpeg4:mbd=2:mv0:trell:v4mv:cbp:last_pred=3:predia=2:dia=2:vmax_b_frames=2:vb_strategy=1:precmp=2:cmp=2:subcmp=2:preme=2:vme=5:naq:qns=2
+ if (false) // does not help
+ // if (pCtx->codec_id == CODEC_ID_MPEG4)
+ {
+ pCtx->mb_decision = 2;
+ pCtx->last_predictor_count = 3;
+ pCtx->pre_dia_size = 2;
+ pCtx->dia_size = 2;
+ pCtx->max_b_frames = 2;
+ pCtx->b_frame_strategy = 2;
+ pCtx->trellis = 2;
+ pCtx->compression_level = 2;
+ pCtx->global_quality = 300;
+ pCtx->pre_me = 2;
+ pCtx->mv0_threshold = 1;
+ // pCtx->quantizer_noise_shaping = 2; // deprecated
+ // TODO
+ }
+
+ pCtx->time_base = (AVRational){1, 25}; /////TODO FIX TO SUPPORT OTHER RATES
+ // pCtx->time_base = (AVRational){1, 10};
+ pCtx->gop_size = 12; // emit one intra frame every twelve frames
+ // pCtx->max_b_frames = 0;
+ pCtx->pix_fmt = PIX_FMT_YUV420P;
+ if (fmt->flags & AVFMT_GLOBALHEADER)
+ pCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
+
+ if (pCtx->codec_id == CODEC_ID_H264)
+ {
+ // http://stackoverflow.com/questions/3553003/encoding-h-264-with-libavcodec-x264
+ pCtx->coder_type = 1; // coder = 1
+ pCtx->flags|=CODEC_FLAG_LOOP_FILTER; // flags=+loop
+ pCtx->me_cmp|= 1; // cmp=+chroma, where CHROMA = 1
+ // pCtx->partitions|=X264_PART_I8X8+X264_PART_I4X4+X264_PART_P8X8+X264_PART_B8X8; // partitions=+parti8x8+parti4x4+partp8x8+partb8x8
+ pCtx->me_method=ME_HEX; // me_method=hex
+ pCtx->me_subpel_quality = 7; // subq=7
+ pCtx->me_range = 16; // me_range=16
+ pCtx->gop_size = 250; // g=250
+ pCtx->keyint_min = 25; // keyint_min=25
+ pCtx->scenechange_threshold = 40; // sc_threshold=40
+ pCtx->i_quant_factor = 0.71; // i_qfactor=0.71
+ pCtx->b_frame_strategy = 1; // b_strategy=1
+ pCtx->qcompress = 0.6; // qcomp=0.6
+ pCtx->qmin = 10; // qmin=10
+ pCtx->qmax = 51; // qmax=51
+ pCtx->max_qdiff = 4; // qdiff=4
+ pCtx->max_b_frames = 3; // bf=3
+ pCtx->refs = 3; // refs=3
+ // pCtx->directpred = 1; // directpred=1
+ pCtx->trellis = 1; // trellis=1
+ // pCtx->flags2|=CODEC_FLAG2_BPYRAMID+CODEC_FLAG2_MIXED_REFS+CODEC_FLAG2_WPRED+CODEC_FLAG2_8X8DCT+CODEC_FLAG2_FASTPSKIP; // flags2=+bpyramid+mixed_refs+wpred+dct8x8+fastpskip
+ // pCtx->weighted_p_pred = 2; // wpredp=2
+ // libx264-main.ffpreset preset
+ // pCtx->flags2|=CODEC_FLAG2_8X8DCT;
+ // pCtx->flags2^=CODEC_FLAG2_8X8DCT; // flags2=-dct8x8
+ }
+
+ AVCodec * codec = avcodec_find_encoder(pCtx->codec_id);
+ if (NULL == codec)
+ throw std::runtime_error("Unable to find Mpeg4 codec");
+ if (codec->pix_fmts)
+ pCtx->pix_fmt = codec->pix_fmts[0];
+ {
+ //QMutexLocker lock(&decoder::mutex);
+ mutex.lock();
+ if (avcodec_open2(pCtx, codec, NULL) < 0)
+ throw std::runtime_error("Error opening codec");
+ mutex.unlock();
+ }
+
+ /* Get framebuffers */
+ if (! (picture_yuv = avcodec_alloc_frame()) ) // final frame format
+ throw std::runtime_error("");
+ if (! (picture_rgb = avcodec_alloc_frame()) ) // rgb version I can understand easily
+ throw std::runtime_error("");
+ /* the image can be allocated by any means and av_image_alloc() is
+ * just the most convenient way if av_malloc() is to be used */
+ if ( av_image_alloc(picture_yuv->data, picture_yuv->linesize,
+ pCtx->width, pCtx->height, pCtx->pix_fmt, 1) < 0 )
+ throw std::runtime_error("Error allocating YUV frame buffer");
+ if ( av_image_alloc(picture_rgb->data, picture_rgb->linesize,
+ pCtx->width, pCtx->height, PIX_FMT_RGB24, 1) < 0 )
+ throw std::runtime_error("Error allocating RGB frame buffer");
+
+ /* Init scale & convert */
+ if (! (Sctx=sws_getContext(
+ width,
+ height,
+ PIX_FMT_RGB24,
+ pCtx->width,
+ pCtx->height,
+ pCtx->pix_fmt,
+ SWS_BICUBIC,NULL,NULL,NULL)) )
+ throw std::runtime_error("");
+
+//
+//
+// added audio init
+ AVCodec * acodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
+ int ret = avcodec_open2(pCtx, acodec, NULL);
+ if (ret < 0) {
+ throw std::runtime_error("Could not open audio codec:");
+
+ }
+
+ if (pCtx->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE)
+ audio_input_frame_size = 10000;
+ else
+ audio_input_frame_size = pCtx->frame_size; //is coming out at 0?
+
+ audiostep=((float)audio_input_frame_size)/(pCtx->sample_rate);
+
+
+// are we supposed to use the same codeccontext?
+//
+
+ /* open the output file */
+ if (!(fmt->flags & AVFMT_NOFILE))
+ {
+ //QMutexLocker lock(&decoder::mutex);
+ mutex.lock();
+ if (avio_open(&container->pb, file_name, AVIO_FLAG_WRITE) < 0)
+ throw std::runtime_error("Error opening output video file");
+ mutex.unlock();
+ }
+ avformat_write_header(container, NULL);
+}
+
+void libav::encoder::setPixelIntensity(int x, int y, int c, uint8_t value)
+{
+ uint8_t * ptr = picture_rgb->data[0] + y * picture_rgb->linesize[0] + x * 3 + c;
+ *ptr = value;
+}
+
+void libav::encoder::write_frame(float seconds,uint8_t *rgbdata)
+{
+ picture_rgb->data[0]=rgbdata;
+
+ // convert from RGB24 to YUV
+ sws_scale(Sctx, // sws context
+ picture_rgb->data, // src slice
+ picture_rgb->linesize, // src stride
+ 0, // src slice origin y
+ pCtx->height, // src slice height
+ picture_yuv->data, // dst
+ picture_yuv->linesize ); // dst stride
+
+ /* encode the image */
+ // use non-deprecated avcodec_encode_video2(...)
+ AVPacket packet;
+ av_init_packet(&packet);
+ packet.data = NULL;
+ packet.size = 0;
+
+ //no time stamps as is
+ //http://dranger.com/ffmpeg/tutorial07.html
+
+ picture_yuv->pts=(uint64_t)(seconds*timebase);
+
+ int got_packet;
+ int ret = avcodec_encode_video2(pCtx,
+ &packet,
+ picture_yuv,
+ &got_packet);
+ if (ret < 0)
+ throw std::runtime_error("Video encoding failed");
+ if (got_packet)
+ {
+ // std::cout << "encoding frame" << std::endl;
+ int result = av_write_frame(container, &packet);
+ av_destruct_packet(&packet);
+ }
+}
+void libav::encoder::write_frame(float seconds,uint16_t *audiodata){
+ audio_frame = avcodec_alloc_frame();
+ AVPacket pkt = { 0 }; // data and size must be 0;
+ int got_packet, ret;
+ av_init_packet(&pkt);
+ audio_frame->nb_samples = audio_input_frame_size;
+ uint8_t *sampleptr;
+ int bufsize=audio_input_frame_size * av_get_bytes_per_sample(pCtx->sample_fmt) *pCtx->channels;
+ if (audiodata) {
+ sampleptr=(uint8_t*)audiodata;
+ }
+ else {
+ sampleptr=new uint8_t[bufsize];
+ memset(sampleptr,0,bufsize);
+ }
+
+ avcodec_fill_audio_frame(audio_frame, pCtx->channels, pCtx->sample_fmt,
+ sampleptr,
+ audio_input_frame_size *
+ av_get_bytes_per_sample(pCtx->sample_fmt) *
+ pCtx->channels, 0); //;
+
+ audio_frame->pts=(uint64_t)(seconds*timebase);
+
+ ret = avcodec_encode_audio2(pCtx, &pkt, audio_frame, &got_packet);
+ if (!audiodata) {
+ delete[] sampleptr;
+ }
+ if (ret < 0) {
+ throw std::runtime_error("Audio encoding failed");
+ }
+
+ if (!got_packet)
+ return;
+
+ // ? pkt.stream_index = st->index;
+
+ ret = av_interleaved_write_frame(container, &pkt);
+ avcodec_free_frame(&audio_frame);
+}
+
+/* virtual */
+libav::encoder::~encoder()
+{
+ int result = av_write_frame(container, NULL); // flush
+ result = av_write_trailer(container);
+ {
+ //QMutexLocker lock(&decoder::mutex);
+ mutex.lock();
+ avio_close(container->pb);
+ mutex.unlock();
+ }
+ for (int i = 0; i < container->nb_streams; ++i)
+ av_freep(container->streams[i]);
+ av_free(container);
+ container = NULL;
+
+ {
+ //QMutexLocker lock(&decoder::mutex);
+ mutex.lock();
+ avcodec_close(pCtx);
+ mutex.unlock();
+ }
+ av_free(pCtx);
+ pCtx = NULL;
+ av_free(picture_yuv->data[0]);
+ av_free(picture_yuv);
+ picture_yuv = NULL;
+ av_free(picture_rgb->data[0]);
+ av_free(picture_rgb);
+ picture_rgb = NULL;
+}
+
+bool libav::exporter::setup(int w,int h, int bitRate, int frameRate, std::string container){
+
+ maybeInitFFMpegLib();
+
+ this->w=w;
+ this->h=h;
+ this->bitRate=bitRate;
+ this->frameRate=frameRate;
+ this->container=container;
+
+ return true;
+}
+
+bool libav::exporter::record(std::string filename){
+
+ // allocate the output media context //
+ avformat_alloc_output_context2(&oc, NULL, NULL, filename.c_str());
+ if (!oc) {
+ printf("Could not deduce output format from file extension: using MPEG.\n");
+ avformat_alloc_output_context2(&oc, NULL, "mpeg", filename.c_str());
+ }
+ if (!oc) {
+ return false;
+ }
+ fmt = oc->oformat;
+
+ // Add the audio and video streams using the default format codecs
+ // * and initialize the codecs. //
+ video_st = NULL;
+ audio_st = NULL;
+
+ if (fmt->video_codec != AV_CODEC_ID_NONE) {
+ video_st = add_stream(oc, &video_codec, fmt->video_codec);
+ }
+ if (fmt->audio_codec != AV_CODEC_ID_NONE) {
+ audio_st = add_stream(oc, &audio_codec, fmt->audio_codec);
+ }
+
+ //set initial video params
+ video_st->codec->width=w;
+ video_st->codec->height=h;
+ video_st->codec->time_base.num = 1;//codecCtx->ticks_per_frame;
+ video_st->codec->time_base.den = frameRate;
+ video_st->time_base = video_st->codec->time_base;
+ //audioStream->time_base = codecCtx->time_base; //???has the capability of crashing
+
+ video_st->codec->gop_size = 10; /* emit one intra frame every ten frames */
+ video_st->codec->pix_fmt = PIX_FMT_YUV420P;
+
+ // 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_codec, video_st);
+ if (audio_st) {
+ audioframesize=open_audio(oc, audio_codec, audio_st);
+ audiostep=((float)audioframesize)/(audio_st->codec->sample_rate);
+ std::cerr << "opened audio codec with "<<audioframesize<<" frame size and "<<audiostep<<" seconds per frame"<<std::endl;
+ }
+
+
+ av_dump_format(oc, 0, filename.c_str(), 1);
+
+ // open the output file, if needed //
+ if (!(fmt->flags & AVFMT_NOFILE)) {
+ mutex.lock();
+ int ret = avio_open(&oc->pb, filename.c_str(), AVIO_FLAG_WRITE);
+ mutex.unlock();
+ if (ret < 0) {
+ std::cerr <<"Could not open " << filename.c_str() << std::endl;
+ return false;
+ }
+ }
+
+ // Write the stream header, if any. //
+ int ret = avformat_write_header(oc, NULL);
+ if (ret < 0) {
+ //std::cerr <<"Error occurred when opening output file:" << av_err2str(ret) << std::endl;
+ return false;
+ }
+
+ if (frame)
+ frame->pts = 0;
+
+ outputframe=0;
+
+ return true;
+}
+bool libav::exporter::encodeFrame(unsigned char *pixels,uint16_t *samples){
+ // Compute current audio and video time. //
+ if (audio_st)
+ audio_pts = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den;
+ else
+ audio_pts = 0.0;
+
+ 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;
+
+ // write interleaved audio and video frames //
+ if (!video_st || (video_st && audio_st && audio_pts < video_pts)) {
+ write_audio_frame(oc, audio_st, samples);
+ } else {
+ write_video_frame(oc, video_st, pixels);
+
+ frame->pts += av_rescale_q(1, video_st->codec->time_base, video_st->time_base);
+ }
+
+ //std::cerr << "encoded frame " << outputframe << std::endl;
+ outputframe++;
+
+ return true;
+}
+bool libav::exporter::encodeFrame(unsigned char *pixels,AVPacket *audio){
+ // Compute current audio and video time. //
+ if (audio_st)
+ audio_pts = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den;
+ else
+ audio_pts = 0.0;
+
+ 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;
+
+ // write interleaved audio and video frames //
+ if (!video_st || (video_st && audio_st && audio_pts < video_pts)) {
+ write_audio_frame(oc, audio_st, audio);
+ } else {
+ write_video_frame(oc, video_st, pixels);
+
+ frame->pts += av_rescale_q(1, video_st->codec->time_base, video_st->time_base);
+ }
+
+ //std::cerr << "encoded frame " << outputframe << std::endl;
+ outputframe++;
+
+ return true;
+}
+bool libav::exporter::encodeFrame(unsigned char *pixels){
+ video_pts = (double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den;
+ write_video_frame(oc, video_st, pixels);
+ frame->pts += av_rescale_q(1, video_st->codec->time_base, video_st->time_base);
+ outputframe++;
+ return true;
+}
+bool libav::exporter::encodeFrame(uint16_t *samples){
+ audio_pts = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den;
+ write_audio_frame(oc, audio_st, samples);
+ return true;
+}
+void libav::exporter::finishRecord(){
+
+ av_write_trailer(oc);
+ // Close each codec. //
+ if (video_st)
+ close_video(oc, video_st);
+ if (audio_st)
+ close_audio(oc, audio_st);
+
+ if (!(fmt->flags & AVFMT_NOFILE)) {
+ // Close the output file. //
+ mutex.lock();
+ avio_close(oc->pb);
+ mutex.unlock();
+ }
+
+ // free the stream //
+ avformat_free_context(oc);
+}
+
+AVStream* libav::exporter::add_stream(AVFormatContext *oc, AVCodec **codec,enum AVCodecID codec_id)
+ {
+ AVCodecContext *c;
+ AVStream *st;
+
+ // find the encoder //
+ *codec = avcodec_find_encoder(codec_id);
+ if (!(*codec)) {
+ //fprintf(stderr, "Could not find encoder for '%s'\n",
+ // avcodec_get_name(codec_id));
+ exit(1);
+ }
+
+ st = avformat_new_stream(oc, *codec);
+ if (!st) {
+ //fprintf(stderr, "Could not allocate stream\n");
+ exit(1);
+ }
+ st->id = oc->nb_streams-1;
+ c = st->codec;
+
+ switch ((*codec)->type) {
+ case AVMEDIA_TYPE_AUDIO:
+ st->id = 1;
+ c->sample_fmt = AV_SAMPLE_FMT_S16;
+ c->bit_rate = 64000;
+ c->sample_rate = 44100;
+ c->channels = 2;
+ c->channel_layout=AV_CH_LAYOUT_STEREO;
+ break;
+
+ case AVMEDIA_TYPE_VIDEO:
+ c->codec_id = codec_id;
+
+ c->bit_rate = 400000;
+ // Resolution must be a multiple of two. //
+ c->width = 352;
+ c->height = 288;
+ // timebase: 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
+ // * identical to 1. //
+ c->time_base.den = frameRate;
+ c->time_base.num = 1;
+ c->gop_size = 12; // emit one intra frame every twelve frames at most //
+ c->pix_fmt = AV_PIX_FMT_YUV420P; //ADDED HARDCODED TJR 280513
+ if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
+ // just for testing, we also add B frames //
+ c->max_b_frames = 2;
+ }
+ if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) {
+ // Needed to avoid using macroblocks in which some coeffs overflow.
+ // * This does not happen with normal video, it just happens here as
+ // * the motion of the chroma plane does not match the luma plane. //
+ c->mb_decision = 2;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // Some formats want stream headers to be separate. //
+ if (oc->oformat->flags & AVFMT_GLOBALHEADER)
+ c->flags |= CODEC_FLAG_GLOBAL_HEADER;
+
+ return st;
+ }
+
+void libav::exporter::open_video(AVFormatContext *oc, AVCodec *codec, AVStream *st)
+ {
+ int ret;
+ AVCodecContext *c = st->codec;
+
+ // open the codec //
+ mutex.lock();
+ ret = avcodec_open2(c, codec, NULL);
+ mutex.unlock();
+ if (ret < 0) {
+ //fprintf(stderr, "Could not open video codec: %s\n", av_err2str(ret));
+ exit(1);
+ }
+
+ // allocate and init a re-usable frame //
+ frame = avcodec_alloc_frame();
+ if (!frame) {
+ //fprintf(stderr, "Could not allocate video frame\n");
+ exit(1);
+ }
+
+ // Allocate the encoded raw picture. //
+ ret = avpicture_alloc(&dst_picture, c->pix_fmt, c->width, c->height);
+ if (ret < 0) {
+ //fprintf(stderr, "Could not allocate picture: %s\n", av_err2str(ret));
+ exit(1);
+ }
+
+ // If the output format is not YUV420P, then a temporary YUV420P
+ // * picture is needed too. It is then converted to the required
+ // * output format. //
+ if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
+ ret = avpicture_alloc(&src_picture, AV_PIX_FMT_RGB24, c->width, c->height);
+ if (ret < 0) {
+ //fprintf(stderr, "Could not allocate temporary picture: %s\n",
+ // av_err2str(ret));
+ exit(1);
+ }
+ }
+
+ // copy data and linesize picture pointers to frame //
+ *((AVPicture *)frame) = dst_picture;
+
+ outPixels = (uint8_t*)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, st->codec->width,st->codec->height));
+ }
+
+ int libav::exporter::open_audio(AVFormatContext *oc, AVCodec *codec, AVStream *st)
+ {
+ AVCodecContext *c;
+ int ret;
+
+ c = st->codec;
+
+ // open it //
+ mutex.lock();
+ ret = avcodec_open2(c, codec, NULL);
+ mutex.unlock();
+ if (ret < 0) {
+ //fprintf(stderr, "Could not open audio codec: %s\n", av_err2str(ret));
+ exit(1);
+ }
+
+ // init signal generator //
+ t = 0;
+ tincr = 2 * M_PI * 110.0 / c->sample_rate;
+ // increment frequency by 110 Hz per second //
+ tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate;
+
+ if (c->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE)
+ audio_input_frame_size = 10000;
+ else
+ audio_input_frame_size = c->frame_size;
+
+ /*
+ samples = av_malloc(audio_input_frame_size *
+ av_get_bytes_per_sample(c->sample_fmt) *
+ c->channels);
+ if (!samples) {
+ //fprintf(stderr, "Could not allocate audio samples buffer\n");
+ exit(1);
+ }
+ */
+ return audio_input_frame_size;
+ }
+
+ void libav::exporter::write_audio_frame(AVFormatContext *oc, AVStream *st,uint16_t *samples)
+ {
+ AVCodecContext *c;
+ AVPacket pkt = { 0 }; // data and size must be 0;
+ AVFrame *frame = avcodec_alloc_frame();
+ int got_packet, ret;
+
+ av_init_packet(&pkt);
+ c = st->codec;
+
+ //get_audio_frame(samples, audio_input_frame_size, c->channels);
+ frame->nb_samples = audio_input_frame_size;
+ uint8_t *sampleptr;
+ int bufsize=audio_input_frame_size * av_get_bytes_per_sample(c->sample_fmt) *c->channels;
+ if (samples) {
+ sampleptr=(uint8_t*)samples;
+ }
+ else {
+ sampleptr=new uint8_t[bufsize];
+ memset(sampleptr,0,bufsize);
+ }
+
+ avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt,
+ sampleptr,
+ audio_input_frame_size *
+ av_get_bytes_per_sample(c->sample_fmt) *
+ c->channels, 0); //;
+ //frame->sample_rate=44100; //hard coded input rate- nope, this doesn't help
+ //frame->format=AV_SAMPLE_FMT_S16P;
+ //?? why is ffmpeg reporting fltp as the sample format??? doesn't seem to have an effect to change this though
+ ret = avcodec_encode_audio2(c, &pkt, frame, &got_packet);
+ if (!samples) {
+ delete[] sampleptr;
+ }
+ if (ret < 0) {
+ //fprintf(stderr, "Error encoding audio frame: %s\n", av_err2str(ret));
+ exit(1);
+ }
+
+ if (!got_packet)
+ return;
+
+ pkt.stream_index = st->index;
+
+ // Write the compressed frame to the media file. //
+ ret = av_interleaved_write_frame(oc, &pkt);
+ if (ret != 0) {
+ //fprintf(stderr, "Error while writing audio frame: %s\n",
+ // av_err2str(ret));
+ exit(1);
+ }
+ avcodec_free_frame(&frame);
+ }
+
+ void libav::exporter::write_audio_frame(AVFormatContext *oc, AVStream *st,AVPacket *pkt)
+ {
+ /*
+ AVCodecContext *c;
+ AVPacket pkt = { 0 }; // data and size must be 0;
+ AVFrame *frame = avcodec_alloc_frame();
+ int got_packet, ret;
+
+ av_init_packet(&pkt);
+ c = st->codec;
+
+ //get_audio_frame(samples, audio_input_frame_size, c->channels);
+ frame->nb_samples = audio_input_frame_size;
+ uint8_t *sampleptr;
+ int bufsize=audio_input_frame_size * av_get_bytes_per_sample(c->sample_fmt) *c->channels;
+ if (samples) {
+ sampleptr=(uint8_t*)samples;
+ }
+ else {
+ sampleptr=new uint8_t[bufsize];
+ memset(sampleptr,0,bufsize);
+ }
+ avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt,
+ sampleptr,
+ audio_input_frame_size *
+ av_get_bytes_per_sample(c->sample_fmt) *
+ c->channels, 1);
+
+ ret = avcodec_encode_audio2(c, &pkt, frame, &got_packet);
+ if (!samples) {
+ free(sampleptr);
+ }
+ if (ret < 0) {
+ //fprintf(stderr, "Error encoding audio frame: %s\n", av_err2str(ret));
+ exit(1);
+ }
+
+ if (!got_packet)
+ return;
+ */
+
+ pkt->stream_index = st->index;
+
+ // Write the compressed frame to the media file. //
+ int ret = av_interleaved_write_frame(oc, pkt);
+ if (ret != 0) {
+ //fprintf(stderr, "Error while writing audio frame: %s\n",
+ // av_err2str(ret));
+ exit(1);
+ }
+ //avcodec_free_frame(&frame);
+ av_free_packet(pkt);
+ }
+
+ void libav::exporter::close_audio(AVFormatContext *oc, AVStream *st)
+ {
+ mutex.lock();
+ avcodec_close(st->codec);
+ mutex.unlock();
+
+ }
+
+ void libav::exporter::write_video_frame(AVFormatContext *oc, AVStream *st, uint8_t *pixels)
+ {
+ int ret;
+ static struct SwsContext *sws_ctx;
+ AVCodecContext *c = st->codec;
+
+/*
+ if (frame_count >= STREAM_NB_FRAMES) {
+ // No more frames to compress. The codec has a latency of a few
+ // * frames if using B-frames, so we get the last frames by
+ // * passing the same picture again. //
+ } else {
+ if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
+ // as we only generate a YUV420P picture, we must convert it
+ // * to the codec pixel format if needed //
+ if (!sws_ctx) {
+ sws_ctx = sws_getContext(c->width, c->height, AV_PIX_FMT_YUV420P,
+ c->width, c->height, c->pix_fmt,
+ sws_flags, NULL, NULL, NULL);
+ if (!sws_ctx) {
+ //fprintf(stderr,
+ // "Could not initialize the conversion context\n");
+ exit(1);
+ }
+ }
+ fill_yuv_image(&src_picture, frame_count, c->width, c->height);
+ sws_scale(sws_ctx,
+ (const uint8_t * const *)src_picture.data, src_picture.linesize,
+ 0, c->height, dst_picture.data, dst_picture.linesize);
+ } else {
+ fill_yuv_image(&dst_picture, frame_count, c->width, c->height);
+ }
+ }
+*/
+ //always convert RGB to YUV
+ //should be context allocated once per render instead of per frame??
+ //
+ //
+ sws_ctx = sws_getContext(c->width, c->height, AV_PIX_FMT_RGB24,
+ c->width, c->height, AV_PIX_FMT_YUV420P,
+ sws_flags, NULL, NULL, NULL);
+
+ avpicture_fill(&src_picture, pixels, PIX_FMT_RGB24, c->width,c->height);
+ //avpicture_fill(&dst_picture, outPixels, PIX_FMT_YUV420P, c->width,c->height);
+
+ sws_scale(sws_ctx, src_picture.data, src_picture.linesize, 0, c->height, dst_picture.data, dst_picture.linesize);
+ //fill_yuv_image(&dst_picture, frame_count, c->width, c->height);
+ if (oc->oformat->flags & AVFMT_RAWPICTURE) {
+ // Raw video case - directly store the picture in the packet //
+ AVPacket pkt;
+ av_init_packet(&pkt);
+
+ pkt.flags |= AV_PKT_FLAG_KEY;
+ pkt.stream_index = st->index;
+ pkt.data = dst_picture.data[0];
+ pkt.size = sizeof(AVPicture);
+
+ ret = av_interleaved_write_frame(oc, &pkt);
+ } else {
+ AVPacket pkt = { 0 };
+ int got_packet;
+ av_init_packet(&pkt);
+
+ // encode the image //
+ ret = avcodec_encode_video2(c, &pkt, frame, &got_packet);
+ if (ret < 0) {
+ //fprintf(stderr, "Error encoding video frame: %s\n", av_err2str(ret));
+ exit(1);
+ }
+ // If size is zero, it means the image was buffered. //
+
+ if (!ret && got_packet && pkt.size) {
+ pkt.stream_index = st->index;
+
+ // Write the compressed frame to the media file. //
+ ret = av_interleaved_write_frame(oc, &pkt);
+ } else {
+ ret = 0;
+ }
+ }
+
+ //
+ // added 22 may in memory leak run
+ //
+ sws_freeContext(sws_ctx); //should be done once per render instead of per frame??
+
+ if (ret != 0) {
+ //fprintf(stderr, "Error while writing video frame: %s\n", av_err2str(ret));
+ exit(1);
+ }
+ frame_count++;
+
+ //avcodec_free_frame(&frame);
+ }
+
+ void libav::exporter::close_video(AVFormatContext *oc, AVStream *st)
+ {
+ mutex.lock();
+ avcodec_close(st->codec);
+ av_free(src_picture.data[0]);
+ av_free(dst_picture.data[0]);
+ av_free(frame);
+ av_free(outPixels); //SIGSEV here???
+ mutex.unlock();
+ }
+
+bool libav::audioloader::setup(const std::string &filename){
+
+ maybeInitFFMpegLib();
+
+ frame = avcodec_alloc_frame();
+ if (!frame)
+ {
+ std::cout << "Error allocating the frame" << std::endl;
+ return false;
+ }
+
+ formatContext = NULL;
+ mutex.lock();
+ if (avformat_open_input(&formatContext, filename.c_str(), NULL, NULL) != 0)
+ {
+ av_free(frame);
+ std::cout << "Error opening the file" << std::endl;
+ return false;
+ }
+ mutex.unlock();
+
+ if (avformat_find_stream_info(formatContext, NULL) < 0)
+ {
+ mutex.lock();
+ av_free(frame);
+ avformat_close_input(&formatContext);
+ mutex.unlock();
+ std::cout << "Error finding the stream info" << std::endl;
+ return false;
+ }
+
+ audioStream = NULL;
+ for (unsigned int i = 0; i < formatContext->nb_streams; ++i)
+ {
+ if (formatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
+ {
+ audioStream = formatContext->streams[i];
+ break;
+ }
+ }
+
+ if (audioStream == NULL)
+ {
+ mutex.lock();
+ av_free(frame);
+ avformat_close_input(&formatContext);
+ mutex.unlock();
+ std::cout << "Could not find any audio stream in the file" << std::endl;
+ return false;
+ }
+
+ codecContext = audioStream->codec;
+
+ codecContext->codec = avcodec_find_decoder(codecContext->codec_id);
+ mutex.lock();
+ if (codecContext->codec == NULL)
+ {
+
+ av_free(frame);
+ avformat_close_input(&formatContext);
+ std::cout << "Couldn't find a proper decoder" << std::endl;
+ return false;
+ }
+ else if (avcodec_open2(codecContext, codecContext->codec, NULL) != 0)
+ {
+
+ av_free(frame);
+ avformat_close_input(&formatContext);
+ mutex.lock();
+ std::cout << "Couldn't open the context with the decoder" << std::endl;
+ return false;
+ }
+ mutex.unlock();
+
+ av_dump_format(formatContext, 0, 0, false); //avformat.h line 1256
+ int samples = ((formatContext->duration + 5000)*codecContext->sample_rate)/AV_TIME_BASE;
+
+ std::cout << "This stream has " << codecContext->channels << " channels, a sample rate of " << codecContext->sample_rate << "Hz and "<<samples <<" samples" << std::endl;
+ std::cout << "The data is in format " <<codecContext->sample_fmt<< " (aka "<< av_get_sample_fmt_name(codecContext->sample_fmt) << ") "<<std::endl;
+
+ av_init_packet(&packet);
+ //sample_processed=0;
+ ready=true;
+ return true;
+ }
+
+ AVFrame* libav::audioloader::get_frame() {
+
+ if (!ready) return nullptr;
+
+ int frameFinished = 0;
+ while (!frameFinished) {
+ int ret=av_read_frame(formatContext, &packet);
+ if (ret<0) {
+ std::cerr << "finished with code "<<ret <<(ret==AVERROR_EOF?" ,EOF":"")<<std::endl;
+ ready=false;
+ return nullptr;
+ }
+ if (packet.stream_index == audioStream->index)
+ {
+ //int bytes =
+ avcodec_decode_audio4(codecContext, frame, &frameFinished, &packet);
+
+ // Some frames rely on multiple packets, so we have to make sure the frame is finished before
+ // we can use it
+ }
+ // You *must* call av_free_packet() after each call to av_read_frame() or else you'll leak memory
+ av_free_packet(&packet);
+ }
+ return frame;
+
+ }
+ AVPacket* libav::audioloader::get_packet() {
+
+ if (!ready) return nullptr;
+
+ int ret=av_read_frame(formatContext, &packet);
+ if (ret<0) {
+ std::cerr << "finished with code "<<ret <<(ret==AVERROR_EOF?" ,EOF":"")<<std::endl;
+ ready=false;
+ return nullptr;
+ }
+ //if (packet.stream_index == audioStream->index)
+ //{
+ //int bytes =
+ // avcodec_decode_audio4(codecContext, frame, &frameFinished, &packet);
+
+ // Some frames rely on multiple packets, so we have to make sure the frame is finished before
+ // we can use it
+ //}
+ // You *must* call av_free_packet() after each call to av_read_frame() or else you'll leak memory
+ //av_free_packet(&packet);?????
+ //}
+ return &packet;
+
+ }
+ uint16_t* libav::audioloader::get_samples(int num){ //presumes 16bpc here
+ //std::cerr << "request "<<num<<" samples: "<<(ready?"ready":"not ready")<<std::endl;
+ //if(!ready) return nullptr;
+ //shuffle down samples
+
+ if (sample_start>0){
+ for (int i=0;i<sample_end-sample_start;i++){
+ for (int j=0;j<channels;j++) {
+ buffer[(i*channels)+j]=buffer[((sample_start+i)*channels)+j];
+ }
+ }
+ sample_start=sample_end-sample_start;
+ }
+
+ sample_end=sample_start;
+ while (sample_end<num) {
+ frame=get_frame();
+ if (frame) {
+ channels=av_frame_get_channels(frame); //will always reach here 1st
+ if (((sample_end+std::max(num,frame->nb_samples))*channels)>buffer.size()){
+ int m=buffer.size();
+ int s=((sample_end+std::max(num,frame->nb_samples))*channels);
+ buffer.reserve(s);
+ std::cerr << "audioloader reserved buffer to " << s << std::endl;
+ for (int i=m;i<s;i++) buffer.push_back(0);
+ }
+ for (int i=0;i<frame->nb_samples;i++) {
+ for (int j=0;j<channels;j++) {
+ //int frame->format
+ //format of the frame, -1 if unknown or unset Values correspond to enum AVPixelFormat for video frames, enum AVSampleFormat for audio)
+ int ff=frame->format;
+ //uint64_t frame->channel_layout
+ //Channel layout of the audio data.
+ uint64_t fcl=frame->channel_layout;
+ //int frame->nb_extended_buf
+ //Number of elements in extended_buf.
+ int fnb=frame->nb_extended_buf;
+ //int frame->decode_error_flags
+ //decode error flags of the frame, set to a combination of FF_DECODE_ERROR_xxx flags if the decoder produced a frame, but there were errors during the decoding.
+ int fde=frame->decode_error_flags;
+
+
+ //uint16_t s=((uint16_t*) frame->buf[j]->data)[i];
+ uint16_t s=((uint16_t*) frame->buf[0]->data)[j*channels+i];
+ //which? must be determined by format or layout of the channels
+ //ALSO some kind of HEINOUS memory leak??
+ buffer[((sample_end+i)*frame->channels)+j]=s;
+ //buffer[(j*frame->channels)+(sample_end+i)]= ((uint16_t*) frame->buf[j]->data)[i]; ??planar?? nope
+ }
+ }
+ sample_end+=frame->nb_samples;
+ }
+ else {
+ for (int i=sample_end;i<num;i++){
+ for (int j=0;j<channels;j++) {
+ buffer[(channels*i)+j]=0;
+ }
+ }
+ sample_end=num;
+ }
+ //std::cerr<<"filling buffer to "<<((sample_end+frame->nb_samples)*frame->channels)<<std::endl;
+
+
+ //avcodec_free_frame(&frame);
+ }
+ if (sample_end>num) {
+ sample_start=num;
+ }
+ else {
+ sample_start=0;
+ }
+ return (uint16_t*)(&buffer[0]);
+}
+
+bool libav::audioloader::close() {
+ mutex.lock();
+ av_free(frame);
+ avcodec_close(codecContext);
+ avformat_close_input(&formatContext);
+ mutex.unlock();
+
+ return true;
+} \ No newline at end of file