From 89961817f408e921de2c1be6197e2b1ee0f5df98 Mon Sep 17 00:00:00 2001 From: Comment Date: Wed, 29 Jan 2014 23:58:55 +0000 Subject: NT audio framework --- NT/src/libavwrapper.cpp | 724 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 724 insertions(+) create mode 100644 NT/src/libavwrapper.cpp (limited to 'NT/src/libavwrapper.cpp') diff --git a/NT/src/libavwrapper.cpp b/NT/src/libavwrapper.cpp new file mode 100644 index 0000000..4a8315f --- /dev/null +++ b/NT/src/libavwrapper.cpp @@ -0,0 +1,724 @@ +#include "libavwrapper.h" + +Poco::Mutex mutex; + +#include +#include +#include + +using namespace std; +using namespace Poco; + +static bool b_is_one_time_inited=false; +static int sws_flags = SWS_BICUBIC; + +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 (uint32_t 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; + + av_opt_set(oc->priv_data, "profile", "baseline", AV_OPT_SEARCH_CHILDREN); + + + // 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_ID_AAC; //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; + + if (bitRate){ + cerr<<"Libav::exporter setting bitrate to "<codec->bit_rate = bitRate; //need to deal with resolution etc + video_st->codec->rc_max_rate = bitRate; + video_st->codec->rc_min_rate = 0; + + //added 131113 + video_st->codec->rc_buffer_size = bitRate * 2; + } + + //av_dict_set(&options, "profile", "baseline", 0); + //video_st->codec->flags2 = CODEC_FLAG2_FASTPSKIP; + video_st->codec->profile = FF_PROFILE_H264_BASELINE; + video_st->codec->level = 13; + + + //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=((double)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 + + // https://libav.org/avprobe.html#MOV_002fMP4_002fISMV + + //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); //makes a video watchable in chrome, quicktime & mplayer + //av_dict_set(&opts, "movflags","empty_moov",1); + } + + + // 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){ + 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) + { + //removed some of the repeats from record() 141113 + 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 = 160000; + 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, avcodec_open2 returned "<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) + { + 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(); + } \ No newline at end of file -- cgit v1.2.3