diff options
| author | Comment <tim@gray.(none)> | 2014-01-29 23:58:55 +0000 |
|---|---|---|
| committer | Comment <tim@gray.(none)> | 2014-01-29 23:58:55 +0000 |
| commit | 89961817f408e921de2c1be6197e2b1ee0f5df98 (patch) | |
| tree | 983f41082fbe2877d9eed913d07784f157d5ced4 /NT/src/libavwrapper.cpp | |
| parent | 90a237397507bda5a8194b9a7c9982454cc79718 (diff) | |
NT audio framework
Diffstat (limited to 'NT/src/libavwrapper.cpp')
| -rw-r--r-- | NT/src/libavwrapper.cpp | 724 |
1 files changed, 724 insertions, 0 deletions
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 <stdexcept> +#include <iostream> +#include <cassert> + +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: "<<filename<<" does not exist"<<endl; + return false; + } + + //first check if an index object exists + Poco::StringTokenizer tokens(filename,"."); + string idxfile=""; + if (tokens.count()>1){ + for (uint32_t i=0;i<tokens.count()-1;i++){ + idxfile+=tokens[i]; + idxfile+="."; + } + idxfile+="idx"; + } + else idxfile=filename+".idx"; + + f=Poco::File(idxfile); + bool makeindex=true; + FFMS_Index *index; + if (f.exists()) { + index=FFMS_ReadIndex(idxfile.c_str(),&err); + if (index) { + cerr<<"FFMS2: loaded index "<<idxfile<<endl; + if (FFMS_IndexBelongsToFile(index,filename.c_str(),&err)==0){ + makeindex=false; + } + } + } + if (makeindex) { + index = FFMS_MakeIndex(filename.c_str(), 0, 0, NULL, NULL, FFMS_IEH_IGNORE, NULL, NULL, &err); + if (index) { + FFMS_WriteIndex(idxfile.c_str(),index,&err); + cerr<<"FFMS2: created index "<<idxfile<<endl; + } + cerr<<"FFMS2: "<<filename<<" cannot be indexed "<<endl; + } + if (index == NULL) { + std::cerr<<"ffmpegsource: "<<err.Buffer<<std::endl; + return false; + } + int trackno = FFMS_GetFirstTrackOfType(index, FFMS_TYPE_VIDEO, &err); + if (trackno < 0) { + std::cerr<<"ffmpegsource: "<<err.Buffer<<std::endl; + return false; + } + source = FFMS_CreateVideoSource(filename.c_str(), trackno, index, 1, FFMS_SEEK_NORMAL, &err); + if (source == NULL) { + std::cerr<<"ffmpegsource: "<<err.Buffer<<std::endl; + return false; + } + FFMS_DestroyIndex(index); + props = FFMS_GetVideoProperties(source); + const FFMS_Frame *propframe = FFMS_GetFrame(source, 0, &err); + w=propframe->EncodedWidth; + h=propframe->EncodedHeight; + //propframe->EncodedPixelFormat; + + if (FFMS_SetOutputFormatV2(source, pixfmts, propframe->EncodedWidth, propframe->EncodedHeight, FFMS_RESIZER_BICUBIC, &err)) { + std::cerr<<"ffmpegsource: "<<err.Buffer<<std::endl; + return false; + } + + //std::cerr<<"ffmpegsource: successfully opened "<<filename<<std::endl; + loaded=true; + return loaded; +} + +bool libav::audio_decoder::open(const std::string& filename){ + Mutex::ScopedLock lock(mutex); + loaded=false; + FFMS_Index *index = FFMS_MakeIndex(filename.c_str(),-1, 0, NULL, NULL, FFMS_IEH_IGNORE, NULL, NULL, &err); + if (index == NULL) { + std::cerr<<"ffmpegsource error making index for "<<filename<<":"<<err.Buffer<<std::endl; + return false; + } + int trackno = FFMS_GetFirstTrackOfType(index, FFMS_TYPE_AUDIO, &err); + if (trackno < 0) { + std::cerr<<"ffmpegsource error finding audio track in "<<filename<<":"<<err.Buffer<<std::endl; + return false; + } + std::cerr<<"ffmpegsource found audio track "<<trackno<<" in "<<filename<<":"<<err.Buffer<<std::endl; + source = FFMS_CreateAudioSource(filename.c_str(), trackno, index, FFMS_DELAY_TIME_ZERO, &err); + if (source == NULL) { + std::cerr<<"ffmpegsource error creating audio source from "<<filename<<":"<<err.Buffer<<std::endl; + return false; + } + FFMS_DestroyIndex(index); + props = FFMS_GetAudioProperties(source); + + std::cerr<<"ffmpegsource: successfully opened "<<filename<<std::endl; + loaded=true; + return loaded; +} + void libav::audio_decoder::cleanup(){ + if (loaded) { + Mutex::ScopedLock lock(mutex); + FFMS_DestroyAudioSource(source); + loaded=false; + } +} + +bool libav::exporter::setup(int w,int h, int bitRate, int frameRate, std::string container, bool _fragmentation){ + + maybeInitFFMpegLib(); + + fragmentation=_fragmentation; + + 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; + + 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 "<<bitRate<<endl; + + 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; + + //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 "<<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::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:" <<endl; // av_err2str(ret) << std::endl; + return false; + } + //#include <libavformat/movenc.h> + //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 " <<endl; // << 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,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 "<<ret<<endl; + return false; + } + + // allocate and init a re-usable frame // + frame = avcodec_alloc_frame(); + + // moved to constructor and freeing in destructor -- stills crashes the same + if (!frame) { + cerr<<"Could not allocate video frame "<<endl; + return false; + } + + // Allocate the encoded raw picture. // + ret = avpicture_alloc(&dst_picture, c->pix_fmt, c->width, c->height); + if (ret < 0) { + //av_err2str(ret)<< + cerr<<"Could not allocate picture"<<endl; + return false; + } + + // 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) { + //<<av_err2str(ret) + cerr<<"Could not allocate temporary picture"<<endl; + return false; + } + } + + // 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)); + 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 |
