#include "libavwrapper.h" extern Poco::Mutex mutex; //application wide mutex static Poco::Mutex mutex; #include #include #include using namespace std; using namespace Poco; 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; } void libav::video_decoder::cleanup(){ if (loaded) { Mutex::ScopedLock lock(mutex); FFMS_DestroyVideoSource(source); loaded=false; } } bool libav::video_decoder::open(const std::string& filename){ cleanup(); Mutex::ScopedLock lock(mutex); Poco::File f=Poco::File(filename); if (!f.exists()) { cerr<<"ERROR: "<1){ for (int i=0;iEncodedWidth; h=propframe->EncodedHeight; //propframe->EncodedPixelFormat; if (FFMS_SetOutputFormatV2(source, pixfmts, propframe->EncodedWidth, propframe->EncodedHeight, FFMS_RESIZER_BICUBIC, &err)) { std::cerr<<"ffmpegsource: "<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 //fmt->audio_codec=AV_CODEC_MP3; //guessing, 011013 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 = 75; /* emit one intra frame every 75 frames */ video_st->codec->pix_fmt = PIX_FMT_YUV420P; //video_st->codec->bit_rate = bitRate; //need to deal with resolution etc //video_st->codec->rc_max_rate = bitRate; //video_st->codec->rc_min_rate = 0; //how to set profile????? 190913 //this causes a crash //av_opt_set(video_st->codec->priv_data,"profile",FF_PROFILE_H264_CONSTRAINED_BASELINE, 0); // // http://libav-users.943685.n4.nabble.com/Libav-user-encoding-bitrate-wrong-td4433740.html // http://libav-users.943685.n4.nabble.com/Libav-user-H264-encoding-set-target-average-bitrate-td3912529.html // http://trac.ffmpeg.org/wiki/x264EncodingGuide // Now that all the parameters are set, we can open the audio and // * video codecs and allocate the necessary encode buffers. // if (video_st) { if (!open_video(oc, video_codec, video_st)){ return false; } } 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::ScopedLock lock(mutex); int ret = avio_open(&oc->pb, filename.c_str(), AVIO_FLAG_WRITE); if (ret < 0) { std::cerr <<"Could not open " << filename.c_str() << std::endl; return false; } } AVDictionary *opts = NULL; // "create" an empty dictionary //THIS DOES SEEM TO SET CONTAINER OPTS= AS MOOV_SIZE MAKES THE MOVIE BANJAXED //av_dict_set(&opts, "moov_size", "20000", 0); if (fragmentation) { av_dict_set(&opts, "movflags","frag_keyframe", 0); } // Write the stream header, if any. // int ret = avformat_write_header(oc, &opts); if (ret < 0) { std::cerr <<"Error occurred when opening output file:" < //mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatContext *s) /*ret = mov_write_moov_tag(oc, NULL,NULL); if (ret < 0) { std::cerr <<"Error occurred when writing moov atom " <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,bool keyframe=false){ video_pts = (double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den; write_video_frame(oc, video_st, pixels,keyframe); 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::ScopedLock lock(mutex); avio_close(oc->pb); } // 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 = bitRate; //need to deal with resolution etc c->rc_max_rate = bitRate; c->rc_min_rate = 0; //c->rc_buffer_size = Profile()->m_videoMaxVopSize; //?? /* all taken out 091113 to try to stop making incompatible movies */ // 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; } bool libav::exporter::open_video(AVFormatContext *oc, AVCodec *codec, AVStream *st) { int ret; AVCodecContext *c = st->codec; // open the codec // { Mutex::ScopedLock lock(mutex); ret = avcodec_open2(c, codec, NULL); //OPTIONS CAN GO HERE } if (ret < 0) { //string e=av_err2str(ret); cerr<<"Could not open video codec"<pix_fmt, c->width, c->height); if (ret < 0) { //av_err2str(ret)<< cerr<<"Could not allocate picture"<pix_fmt != AV_PIX_FMT_YUV420P) { ret = avpicture_alloc(&src_picture, AV_PIX_FMT_RGB24, c->width, c->height); if (ret < 0) { //<codec->width,st->codec->height)); return true; } 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) { 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, bool keyframe=false) { int ret; AVCodecContext *c = st->codec; 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; if (keyframe) pkt.flags |= AV_PKT_FLAG_KEY; 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(); }