diff options
| author | Tim Redfern <tim@eclectronics.org> | 2013-06-12 17:03:57 +0100 |
|---|---|---|
| committer | Tim Redfern <tim@eclectronics.org> | 2013-06-12 17:03:57 +0100 |
| commit | ff76e2487e6d01329a39d4aa1bbb26d4afcdb0de (patch) | |
| tree | eb33f46ed804b41efaa656d1b22a32c5bdd8dc50 /rotord | |
| parent | 78eb4c35ae7ee52615c62abb3683595fe6384206 (diff) | |
file clean
Diffstat (limited to 'rotord')
| -rwxr-xr-x | rotord/libavwrapper.cpp | 1694 | ||||
| -rwxr-xr-x | rotord/libavwrapper.h | 274 | ||||
| -rw-r--r-- | rotord/libavwrapper.o | bin | 0 -> 564024 bytes |
3 files changed, 1968 insertions, 0 deletions
diff --git a/rotord/libavwrapper.cpp b/rotord/libavwrapper.cpp new file mode 100755 index 0000000..be61ae9 --- /dev/null +++ b/rotord/libavwrapper.cpp @@ -0,0 +1,1694 @@ +#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); +} +*/ + +void libav::decoder::cleanup(){ + //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? + +} + +/* virtual */ +libav::decoder::~decoder() +{ + cleanup(); +} +/* +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 + + 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 + fmt->audio_codec = AV_CODEC_ID_MP3; + // fmt->video_codec = CODEC_ID_H264; // fails to write + + audio_st = avformat_new_stream(container, NULL); + + aCtx = audio_st->codec; + aCtx->codec_id = fmt->audio_codec; + aCtx->codec_type = AVMEDIA_TYPE_AUDIO; + + aCtx->sample_fmt=AV_SAMPLE_FMT_S16P; //s16p is invalid or not supported by aac: S16 not by mp3 + aCtx->channels=2; + aCtx->sample_rate=44100; + aCtx->channel_layout=AV_CH_LAYOUT_STEREO; + aCtx->bit_rate = 64000; + + + + AVCodec * acodec = avcodec_find_encoder(aCtx->codec_id); + mutex.lock(); + int ret = avcodec_open2(aCtx, acodec, NULL); + mutex.unlock(); + if (ret < 0) { + throw std::runtime_error("Could not open audio codec:"); + + } + + if (aCtx->codec->capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE) + audio_input_frame_size = 10000; + else + audio_input_frame_size = aCtx->frame_size; //is coming out at 0? + + cerr<<"audio codec frame size is "<<audio_input_frame_size<<endl; + + //if (audio_input_frame_size ==0) { can't do this + // audio_input_frame_size =10000; + //} + + // [mpeg4 @ 0x7f02f00200e0] nb_samples (10000) != frame_size (0) (avcodec_encode_audio2) - quits thread + // why is frame size 0? + // should there be a seperate pCtx for audio? + + if (container->oformat->flags & AVFMT_GLOBALHEADER) + aCtx->flags |= CODEC_FLAG_GLOBAL_HEADER; + + + audiostep=((float)audio_input_frame_size)/(aCtx->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={0}; + 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); + + //packet.pts=(uint64_t)(seconds*timebase); //added 0606 + packet.stream_index = video_st->index;; //added 0606 + + 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(aCtx->sample_fmt) *aCtx->channels; + if (audiodata) { + sampleptr=(uint8_t*)audiodata; + } + else { + sampleptr=new uint8_t[bufsize]; + memset(sampleptr,0,bufsize); + } + + audio_frame->pts=(uint64_t)(seconds*timebase); // + + avcodec_fill_audio_frame(audio_frame, aCtx->channels, aCtx->sample_fmt, + sampleptr, + audio_input_frame_size * + av_get_bytes_per_sample(aCtx->sample_fmt) * + aCtx->channels, 0); //; + + + + ret = avcodec_encode_audio2(aCtx, &pkt, audio_frame, &got_packet); + + pkt.stream_index = audio_st->index; //hardcoded stream index added 0606 + //pkt.pts=(uint64_t)(seconds*timebase); //added 060613 + + 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() +{ + + //avcodec_flush_buffers(pCtx); ???? from exporter version + + + 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(); + + //added 0706 + video_st=nullptr; + audio_st=nullptr; + // + + for (int i = 0; i < container->nb_streams; ++i) { + av_freep(container->streams[i]); //CRASHING HERE ON STREAM 1, OUTPUT IS VALID BUT AUDIO INAUDIBLE - 060613 + } + av_free(container); + container = nullptr; + //QMutexLocker lock(&decoder::mutex); + mutex.lock(); + avcodec_close(aCtx); + avcodec_close(pCtx); + mutex.unlock(); + av_free(pCtx); + pCtx = NULL; + av_free(aCtx); + aCtx=nullptr; + 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; + + fmt->video_codec=AV_CODEC_ID_MPEG4; + + 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(); + // moved to constructor and freeing in destructor -- stills crashes the same + 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; + + 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 // + + // 2nd time you render it crashes right after here + + // where the hell is frame being allocated? is the problem caused by it being freed? (see removeal of avframe_free in cleanup) + 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); //change 0706 to trace 2nd render issue + avcodec_close(audio_st->codec); + avcodec_close(video_st->codec); + // + // + + + //av_free(src_picture.data[0]); //removed to explore weird 2nd render crash.. seems to WORK -- seems that the picture data is owned elsewhere + av_free(dst_picture.data[0]); + av_free(frame); //removed to explore crash 2nd time render + //gives *** Error in `./rotord': corrupted double-linked list: 0x00007fd8b005bd60 *** + //where is frame initialised??? + //moved to destructor + + + 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(); + ready=false; + sample_start=0; + sample_end=0; + return true; +} diff --git a/rotord/libavwrapper.h b/rotord/libavwrapper.h new file mode 100755 index 0000000..faa4a2e --- /dev/null +++ b/rotord/libavwrapper.h @@ -0,0 +1,274 @@ + #ifndef libavwrapper_H +#define libavwrapper_H + +/* + * libavwrapper.h + * May 2012 Christopher Bruns + * The libavwrapper class is a C++ wrapper around the poorly documented + * libavcodec movie API used by ffmpeg. I made extensive use of Nathan + * Clack's implemention in the whisk project. + * + * The libavwrapper.h and libavwrapper.cpp files depend only on the libavcodec + * and allied sets of libraries. To compartmentalize and reduce dependencies + * I placed the Vaa3d specific use of this class into a separate set of + * source files: loadV3dFFMpeg.h/cpp + */ + +//////////////////////// +//now that we have guards +//instead of crashing instantly when the 2nd thread tries to encode a frame, we get an error + + //*** Error in `./rotord': corrupted double-linked list: 0x00007f3c31b1b630 *** + + //or + + //*** Error in `./rotord': double free or corruption (out): 0x00007f3bf8210080 *** + /////////////////////// + + +//http://blog.tomaka17.com/2012/03/libavcodeclibavformat-tutorial/ +//great to use c++11 features + +#ifndef UINT64_C +#define UINT64_C(c) (c ## ULL) +#endif + +#include "Poco/Mutex.h" + +extern "C" { +#include <libavcodec/avcodec.h> +#include <libavformat/avformat.h> +#include <libavutil/pixfmt.h> +#include <libavutil/opt.h> +#include <libavutil/imgutils.h> + +#include <libswscale/swscale.h> //? +} + +/* +#include <QFile> +#include <QNetworkAccessManager> +#include <QMutex> +#include <QUrl> +#include <QBuffer> +*/ + + +#include <string> +#include <stdexcept> +#include <iostream> +#include <fstream> +#include <math.h> +#include <vector> + + + +namespace libav { + + + + static bool b_is_one_time_inited=false; + // Some libavcodec calls are not reentrant + + void maybeInitFFMpegLib(); + + static int sws_flags = SWS_BICUBIC; + +// Translated to C++ by Christopher Bruns May 2012 +// from ffmeg_adapt.c in whisk package by Nathan Clack, Mark Bolstadt, Michael Meeuwisse + class decoder + { + public: + enum Channel { + RED = 0, + GRAY = 0, + GREEN = 1, + BLUE = 2, + ALPHA = 3 + }; + + + decoder(PixelFormat pixelFormat=PIX_FMT_RGB24); + //decoder(QUrl url, PixelFormat pixelFormat=PIX_FMT_RGB24); + void cleanup(); + virtual ~decoder(); + //bool open(QUrl url, enum PixelFormat formatParam = PIX_FMT_RGB24); + //bool open(QIODevice& fileStream, QString& fileName, enum PixelFormat formatParam = PIX_FMT_RGB24); + uint8_t getPixelIntensity(int x, int y, Channel c = GRAY) const; + bool fetchFrame(int targetFrameIndex = 0); + int getNumberOfFrames() const; + int getWidth() const; + int getHeight() const; + int getNumberOfChannels() const; + bool readNextFrame(int targetFrameIndex = 0); + bool readNextFrameWithPacket(int targetFrameIndex, AVPacket& packet, AVFrame* pYuv); + int seekToFrame(int targetFrameIndex = 0); + + // make certain members public, for use by Fast3DTexture class + AVFrame *pFrameRGB; + AVFrame *pRaw; + AVFormatContext *container; + AVCodecContext *pCtx; + int videoStream; + int previousFrameIndex; + bool isOpen; + + bool open(std::string& fileName, enum PixelFormat formatParam = PIX_FMT_RGB24); + bool open(char* fileName, enum PixelFormat formatParam = PIX_FMT_RGB24); + + protected: + + + void initialize(); + + bool openUsingInitializedContainer(enum PixelFormat formatParam = PIX_FMT_RGB24 ); + static bool avtry(int result, const std::string& msg); + + AVCodec *pCodec; + uint8_t *buffer, + *blank; + //struct + SwsContext *Sctx; + int width, height; + PixelFormat format; + size_t numBytes; + int numFrames; + int sc; // number of color channels + + // For loading from URL + /* + static const int ioBufferSize = 32768; + unsigned char * ioBuffer; + QNetworkAccessManager networkManager; + AVIOContext* avioContext; + QFile fileStream; + QNetworkReply* reply; + QBuffer fileBuffer; + QByteArray byteArray; + */ + }; + + + // TODO - finish refactoring based on + // http://svn.gnumonks.org/trunk/21c3-video/ffmpeg/ffmpeg-0.4.9-pre1/output_example.c + class encoder + { + public: + //typedef encoder::Channel Channel; + + encoder(const char * file_name, int width, int height, float _framerate=25.0f, enum AVCodecID codec_id = CODEC_ID_H264); + virtual ~encoder(); + void setPixelIntensity(int x, int y, int c, uint8_t value); + void write_frame(float seconds,uint8_t *rgbdata); + void write_frame(float seconds,uint16_t *audiodata); + int get_audio_framesize(){ return audio_input_frame_size; } + float get_audio_step(){return audiostep;}; + + protected: + AVFormatContext *container; + AVCodecContext *pCtx; + AVFrame *picture_yuv; + AVFrame *picture_rgb; + AVFrame *audio_frame; + float timebase; + struct SwsContext *Sctx; + + AVStream *audio_st; + AVStream *video_st; + + AVCodecContext *aCtx; + int audio_input_frame_size; + float audiostep; + }; + + + class exporter { + public: + virtual ~exporter(){}; + bool setup(int w,int h, int bitRate, int frameRate, std::string container); + bool record(std::string filename); + bool encodeFrame(unsigned char *pixels, uint16_t *samples); + bool encodeFrame(unsigned char *pixels,AVPacket *audiopkt); //is possible to just copy the packets? + bool encodeFrame(unsigned char *pixels); + bool encodeFrame(uint16_t *samples); + void finishRecord(); + int get_audio_framesize(){return audioframesize;}; + float get_audio_step(){return audiostep;}; + + AVStream *add_stream(AVFormatContext *oc, AVCodec **codec,enum AVCodecID codec_id); + void open_video(AVFormatContext *oc, AVCodec *codec, AVStream *st); + int open_audio(AVFormatContext *oc, AVCodec *codec, AVStream *st); + + void write_audio_frame(AVFormatContext *oc, AVStream *st,uint16_t *samples); + void write_audio_frame(AVFormatContext *oc, AVStream *st,AVPacket *pkt); + void close_audio(AVFormatContext *oc, AVStream *st); + + void write_video_frame(AVFormatContext *oc, AVStream *st, uint8_t *pixels); + void close_video(AVFormatContext *oc, AVStream *st); + + private: + AVOutputFormat *fmt; + AVFormatContext *oc; + AVStream *audio_st, *video_st; + AVCodec *audio_codec, *video_codec; + double audio_pts, video_pts; + + struct SwsContext *sws_ctx; + + int audioframesize; + float audiostep; + int w; + int h; + int bitRate; + int frameRate; + std::string container; + + int outputframe; + + // video output // + + AVFrame *frame; + AVPicture src_picture, dst_picture; + int frame_count; + uint8_t *outPixels; + + + //************************************************************// + // audio output // + + float t, tincr, tincr2; + int audio_input_frame_size; + + + }; + + class audioloader{ + public: + audioloader(){ready=false;sample_start=0;sample_end=0;}; + bool setup(const std::string &filename); + AVFrame* get_frame(); + uint16_t* get_samples(int num); + AVPacket* get_packet(); + bool close(); + bool ready; + + AVCodecContext* codecContext; + AVFormatContext* formatContext; + int channels; //necessary to handle final packet -- unititialised after load/ problem? + private: + std::vector<uint16_t> buffer; + AVFrame* frame; + + AVStream* audioStream; + + AVPacket packet; + int sample_end; + int sample_start; + + }; + +} + + + +#endif // libavwrapper_H diff --git a/rotord/libavwrapper.o b/rotord/libavwrapper.o Binary files differnew file mode 100644 index 0000000..3dafb7e --- /dev/null +++ b/rotord/libavwrapper.o |
