#include "libavwrapper.h" extern Poco::Mutex mutex; //application wide mutex static Poco::Mutex mutex; extern "C" { #include } #include #include #include 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 // Avoid link error on some macs #ifdef __APPLE__ extern "C" { #include #include } #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(); } } void libav::maybeInitFFMpegLib() { if (b_is_one_time_inited) return; FFMS_Init(0, 0); //should do for all //av_register_all(); //avcodec_register_all(); avformat_network_init(); b_is_one_time_inited = true; } ///////////////////////////// // 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) { Sctx = NULL; pRaw = NULL; pFrameRGB = NULL; pCtx = NULL; container = NULL; buffer = NULL; blank = NULL; format = pixelFormat; mutex.lock(); initialize(); mutex.unlock(); } void libav::decoder::cleanup(){ 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; } */ // Don't need to free pCodec? } /* virtual */ libav::decoder::~decoder() { cleanup(); } // 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; framerate=(((float)container->streams[videoStream]->r_frame_rate.num)/((float)container->streams[videoStream]->r_frame_rate.den)); //cerr<<"codecContext timebase: "<<(((float)pCtx->time_base.num)/((float)pCtx->time_base.den))<<" videostream framerate: "<<(((float)container->streams[videoStream]->r_frame_rate.num)/((float)container->streams[videoStream]->r_frame_rate.den))<streams[videoStream]->r_frame_rate.num<<"/"<streams[videoStream]->r_frame_rate.den<duration<time_base.num<<"/"<time_base.den<duration / (double)AV_TIME_BASE ) * pCtx->time_base.den + 0.5); //this approach just seems wrong! numFrames=container->streams[videoStream]->nb_frames-1; if (numFrames<1){ //some codecs don't keep this info in the header numFrames = (int)(( container->duration / (double)AV_TIME_BASE ) * framerate ); //this approach still doesn't seem to give quite the right answer- comes out a little too big //could alternatively just redefine the length if the reader fails } init_buffers_and_scaler(); /* Give some info on stderr about the file & stream */ //dump_format(container, 0, fname, 0); previousFrameIndex = -1; return true; } bool libav::decoder::reinit_buffers_and_scaler(){ 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 != buffer) { av_free(buffer); buffer = NULL; } if (NULL != blank) { av_free(blank); blank = NULL; } mutex.unlock(); init_buffers_and_scaler(); } bool libav::decoder::init_buffers_and_scaler(){ /* 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, width, 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, width, 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(""); } } bool libav::decoder::fetchFrame(int w, int h,int targetFrameIndex) { if (w!=width||h!=height){ width=w; height=h; cerr<<"libav::decoder reiniting to "< numFrames)) return false; if (targetFrameIndex == (previousFrameIndex + 1)) { if (! readNextFrame(targetFrameIndex+1)) //frame indexing starts at 1 return false; } else { int64_t response=seekToFrame(targetFrameIndex+1); //frame indexing starts at 1 if (response < 0) return false; if (response!=targetFrameIndex+1) { cerr<<"libav::decoder asked for "<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 // if (result< 0) return -1; avcodec_flush_buffers(pCtx); //if (! readNextFrame(targetFrameIndex)) // return -1; return targetFrameIndex; } // \returns current frame on success, otherwise -1 int libav::decoder::seekToFrameNew(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 flags = AVSEEK_FLAG_BACKWARD; if (ts > 0 && ts < duration) flags |= AVSEEK_FLAG_ANY; // H.264 I frames don't always register as "key frames" in FFmpeg int ret = av_seek_frame(container, videoStream, ts, flags); if (ret < 0) ret = av_seek_frame(container, videoStream, ts, AVSEEK_FLAG_ANY); if (ret< 0) return -1; return targetFrameIndex; } bool libav::decoder::readNextFrame(int targetFrameIndex) { AVPacket packet = {0}; //av_init_packet(&packet); //moved insode loop 100913 bool result = readNextFrameWithPacket(targetFrameIndex, packet, pRaw); //av_free_packet(&packet); //moved insode loop 100913 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); //moved insode loop 100913 int result; //if (!avtry(av_read_frame( container, &packet ), "Failed to read frame")) if (!avtry(av_read_packet( container, &packet ), "Failed to read packet")) 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, very 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) packet.pts = 0; //throw std::runtime_error(""); //why does it want to throw an error here, isn't the frame succesfully decoded? // //when we allow these packets through we get //[swscaler @ 0x9ef0c80] bad src image pointers //trying to ignore timestamp below if (packet.size == 0) // packet.size==0 usually means EOF break; } av_free_packet(&packet); } while ( (!finished) || (pYuv->best_effort_timestamp < targetFrameIndex)); // } while (!finished); //av_free_packet(&packet); //moved insode loop 100913 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(); } 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<EncodedWidth; h=propframe->EncodedHeight; //propframe->EncodedPixelFormat; if (FFMS_SetOutputFormatV2(source, pixfmts, propframe->EncodedWidth, propframe->EncodedHeight, FFMS_RESIZER_BICUBIC, &err)) { std::cerr<<"ffmpegsource: "<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; 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; if (NULL != sws_ctx) { sws_freeContext(sws_ctx); sws_ctx = NULL; } sws_ctx = sws_getContext(w, h, AV_PIX_FMT_RGB24, w, h, AV_PIX_FMT_YUV420P, sws_flags, NULL, NULL, NULL); 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_H264; //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); 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 "<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 = 256000; 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; //need to deal with resolution etc // 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); 111013 NOT NECESSARY 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; //avcodec_free_frame(&frame); ///removed 091013 // Write the compressed frame to the media file. // ret = av_interleaved_write_frame(oc, &pkt); av_free_packet(&pkt); if (ret != 0) { //fprintf(stderr, "Error while writing audio frame: %s\n", // av_err2str(ret)); exit(1); } } 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); ///added 091013 } 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_get_context was here 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); ///removed 101013 NOT NECESSARY 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); av_free_packet(&pkt); ///added 091013 } else { AVPacket pkt = { 0 }; int got_packet; //av_init_packet(&pkt); ///removed 101013 NOT NECESSARY // 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; } av_free_packet(&pkt); ///added 091013 } 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; mutex.unlock(); 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; } //use the first audio stream found 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); mutex.unlock(); 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.unlock(); 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 "<sample_fmt<< " (aka "<< av_get_sample_fmt_name(codecContext->sample_fmt) << ") "<sample_fmt)==1); if(isPlanar) { cerr<<"found planar audio"<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; } uint16_t* libav::audioloader::get_samples(int num){ //presumes 16bpc here and outputs interleaved samples //std::cerr << "request "<0){ for (int i=0;inb_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;inb_samples;i++) { for (int j=0;jformat //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; if (isPlanar) { s=((uint16_t*) frame->buf[j]->data)[i]; }else { s=((uint16_t*) frame->buf[0]->data)[j*channels+i]; } //where is audio grunge coming from? signed/ unsigned? doesn't seem to be byte order.. // add +1 to data subscript with no effect //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;inb_samples)*frame->channels)<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; }