diff options
| -rw-r--r-- | rotord/src/graph.cpp | 148 | ||||
| -rw-r--r-- | rotord/src/graph.h | 5 | ||||
| -rw-r--r-- | rotord/src/libavwrapper.cpp | 66 | ||||
| -rw-r--r-- | rotord/src/libavwrapper.h | 107 | ||||
| -rw-r--r-- | rotord/src/rotor.h | 16 |
5 files changed, 295 insertions, 47 deletions
diff --git a/rotord/src/graph.cpp b/rotord/src/graph.cpp index 5487717..29b46be 100644 --- a/rotord/src/graph.cpp +++ b/rotord/src/graph.cpp @@ -106,6 +106,154 @@ bool Graph::video_render(const string &output_filename,const float framerate) { if (exporter.setup(outW,outH,bitRate,framerate,container)) { //codecId, if (exporter.record(output_filename)) { + libav::audio_decoder audioloader; + + bool usingaudio=audioloader.open(audio_filename); + float *avframe=nullptr; + + Logger& logger = Logger::get("Rotor"); + logger.information("Video_output rendering "+output_filename+": "+toString(duration)+" seconds at "+toString(framerate)+" fps, audio frame size: "+toString(exporter.get_audio_framesize())); + //25fps video and 43.06640625fps audio? hmm + //how to get the timecodes correct for the interleaved files + + struct timeval start, end; + + gettimeofday(&start, NULL); + + uint16_t *audioframe=nullptr; + uint16_t *audio=nullptr; + int samples_in_frame; + + if (usingaudio){ + samples_in_frame=(audioloader.get_sample_rate())/framerate; + string whether=usingaudio?"Loading":"Cannot load"; + logger.information(whether+" audio file: "+audio_filename+", each frame contains "+toString(samples_in_frame)+" samples at "+toString(audioloader.get_sample_rate())+" hz"); + audioframe=new uint16_t[(samples_in_frame+exporter.get_audio_framesize())*audioloader.get_number_channels()]; + audio=new uint16_t[samples_in_frame*audioloader.get_number_channels()]; + } + + float vstep=1.0f/framerate; + float v=0.0f; + float vf=0.0f; + float af=0.0f; + int aoffs=0; + int audioend=0; + Audio_frame *a; + int64_t sample_start=0; + while (vf<duration&&!cancelled){ //-vstep) { + + if (usingaudio) { + if (audioloader.get_samples(audio,sample_start,samples_in_frame)) { + if (aoffs>0){ + //shift down samples + int s=0; + while ((s+aoffs)<audioend) { + for (int j=0;j<audioloader.get_number_channels();j++){ + audioframe[s*audioloader.get_number_channels()+j]=audioframe[(s+aoffs)*audioloader.get_number_channels()+j]; + } + s++; + } + aoffs=s; + } + for (int i=0;i<samples_in_frame;i++){ + for (int j=0;j<audioloader.get_number_channels();j++){ + audioframe[(aoffs+i)*audioloader.get_number_channels()+j]=audio[i*audioloader.get_number_channels()+j]; + } + } + audioend=aoffs+samples_in_frame; + aoffs=0; + while (aoffs+exporter.get_audio_framesize()<audioend) { + //insert audio frames until we are only 1 audio frame behind the next video frame + //send audio_framesize() of them through until buffer is used + //pass full buffer within frame_spec for av nodes + exporter.encodeFrame(audioframe+(aoffs*audioloader.get_number_channels())); + af+=exporter.get_audio_step(); + aoffs+=exporter.get_audio_framesize(); + } + a=new Audio_frame(audio,audioloader.get_number_channels(),samples_in_frame); + sample_start+=samples_in_frame; + } + + } + + + //[mp3 @ 0x7fffe40330e0] max_analyze_duration 5000000 reached at 5015510 microseconds + //[mp3 @ 0x7fffe4033ec0] Insufficient thread locking around avcodec_open/close() + //[mp3 @ 0x7fffe40330e0] Estimating duration from bitrate, this may be inaccurate + //[libx264 @ 0x7fffe8003940] using cpu capabilities: MMX2 SSE2Fast SSSE3 FastShuffle SSE4.2 + //[libx264 @ 0x7fffe8003940] profile High, level 3.0 + //[libx264 @ 0x7fffe8003940] 264 - core 123 r2189 35cf912 - H.264/MPEG-4 AVC codec - Copyleft 2003-2012 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=12 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=10 keyint_min=1 scenecut=40 intra_refresh=0 rc_lookahead=10 rc=abr mbtree=1 bitrate=400 ratetol=1.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00 + //Assertion ff_avcodec_locked failed at libavcodec/utils.c:2967 + + //cerr<<"videoloader: "<<vf<<" seconds, vstep "<<vstep<<" ,asking for frame "<<((int)((vf*framerate)+0.5))<<endl + + Image* i; + if (usingaudio) { + i=video_output->get_output(Frame_spec(vf,framerate,duration,outW,outH,a)); + } + else i=video_output->get_output(Frame_spec(vf,framerate,duration,outW,outH)); + if (i) { + exporter.encodeFrame(i->RGBdata); + } + vf+=vstep; + progress=vf/duration; + if (usingaudio) {delete a;}; + } + + exporter.finishRecord(); + + gettimeofday(&end, NULL); + + float mtime = ((end.tv_sec-start.tv_sec) + (end.tv_usec-start.tv_usec)/1000000.0) + 0.5; + + logger.information("Video_output: rendered "+output_filename+": in "+toString(mtime)+" seconds"); + + if (usingaudio) { + audioloader.cleanup(); + delete[] audioframe; + delete[] audio; + } + + + + return true; + } + } + + return false; + } + + cerr<<"Rotor: video output node not found"<<endl; + return false; +} + +bool Graph::_video_render(const string &output_filename,const float framerate) { + //vector<Node*> loaders=find_nodes("video_loader"); + //for (auto i:loaders){ + // if (!dynamic_cast<Video_loader*>(i)->isLoaded) { + // cerr<<"Rotor: all loaders must be populated before rendering"<<endl; + // return false; + // } + //} + if (find_node("video_output")) { + Video_output *video_output=dynamic_cast<Video_output*>(find_node("video_output")); + for (auto f: find_nodes("video_feedback")){ + (dynamic_cast<Video_feedback*>(f))->set_feedback(&(video_output->image)); + } + // + //setup defaults + int bitRate=5000000; + AVCodecID codecId=AV_CODEC_ID_H264; //MPEG4; + std::string container ="mp4"; + + //at the moment it crashes if you render before audio is loaded and also on 2nd render + libav::exporter exporter; + + float spct=100.0f/duration; + + if (exporter.setup(outW,outH,bitRate,framerate,container)) { //codecId, + if (exporter.record(output_filename)) { + libav::audioloader audioloader; bool usingaudio=audioloader.setup(audio_filename); diff --git a/rotord/src/graph.h b/rotord/src/graph.h index 6a2fc2d..9207559 100644 --- a/rotord/src/graph.h +++ b/rotord/src/graph.h @@ -42,6 +42,7 @@ namespace Rotor { vector<Node*> find_nodes(const string &type); //could be a way of finding a set based on capabilities? Node* find_node(const string &type); bool signal_render(string &signal_xml,const float framerate); + bool _video_render(const string &output_filename,const float framerate); bool video_render(const string &output_filename,const float framerate); bool load(string data,string media_path); bool loadFile(string &filename,string media_path); @@ -70,7 +71,7 @@ namespace Rotor { public: bool make(const string &inputfilename,int w,int h,const string &outputfilename) { if (player.open(inputfilename)){ - if (player.fetchFrame(w,h,player.getNumberOfFrames()/4)) { + if (player.fetch_frame(w,h,player.get_number_frames()/4)) { Image i; i.setup_fromRGB(w,h,player.frame->Data[0],player.frame->Linesize[0]-(w*3)); cv::Mat cp; @@ -83,7 +84,7 @@ namespace Rotor { return false; } private: - libav::ffms2_decoder player; + libav::video_decoder player; }; } #endif diff --git a/rotord/src/libavwrapper.cpp b/rotord/src/libavwrapper.cpp index 717667c..1fe0e5a 100644 --- a/rotord/src/libavwrapper.cpp +++ b/rotord/src/libavwrapper.cpp @@ -557,45 +557,45 @@ bool libav::decoder::avtry(int result, const std::string& msg) { return true; } - void libav::ffms2_decoder::cleanup(){ + void libav::video_decoder::cleanup(){ if (loaded) { mutex.lock(); - FFMS_DestroyVideoSource(videosource); + FFMS_DestroyVideoSource(source); mutex.unlock(); loaded=false; } } -bool libav::ffms2_decoder::open(const std::string& filename){ +bool libav::video_decoder::open(const std::string& filename){ mutex.lock(); loaded=false; - FFMS_Index *index = FFMS_MakeIndex(filename.c_str(), 0, 0, NULL, NULL, FFMS_IEH_IGNORE, NULL, NULL, &errinfo); + FFMS_Index *index = FFMS_MakeIndex(filename.c_str(), 0, 0, NULL, NULL, FFMS_IEH_IGNORE, NULL, NULL, &err); if (index == NULL) { - std::cerr<<"ffmpegsource: "<<errinfo.Buffer<<std::endl; + std::cerr<<"ffmpegsource: "<<err.Buffer<<std::endl; mutex.unlock(); return false; } - int trackno = FFMS_GetFirstTrackOfType(index, FFMS_TYPE_VIDEO, &errinfo); + int trackno = FFMS_GetFirstTrackOfType(index, FFMS_TYPE_VIDEO, &err); if (trackno < 0) { - std::cerr<<"ffmpegsource: "<<errinfo.Buffer<<std::endl; + std::cerr<<"ffmpegsource: "<<err.Buffer<<std::endl; mutex.unlock(); return false; } - videosource = FFMS_CreateVideoSource(filename.c_str(), trackno, index, 1, FFMS_SEEK_NORMAL, &errinfo); - if (videosource == NULL) { - std::cerr<<"ffmpegsource: "<<errinfo.Buffer<<std::endl; + source = FFMS_CreateVideoSource(filename.c_str(), trackno, index, 1, FFMS_SEEK_NORMAL, &err); + if (source == NULL) { + std::cerr<<"ffmpegsource: "<<err.Buffer<<std::endl; mutex.unlock(); return false; } FFMS_DestroyIndex(index); - videoprops = FFMS_GetVideoProperties(videosource); - const FFMS_Frame *propframe = FFMS_GetFrame(videosource, 0, &errinfo); + props = FFMS_GetVideoProperties(source); + const FFMS_Frame *propframe = FFMS_GetFrame(source, 0, &err); w=propframe->EncodedWidth; h=propframe->EncodedHeight; //propframe->EncodedPixelFormat; - if (FFMS_SetOutputFormatV2(videosource, pixfmts, propframe->EncodedWidth, propframe->EncodedHeight, FFMS_RESIZER_BICUBIC, &errinfo)) { - std::cerr<<"ffmpegsource: "<<errinfo.Buffer<<std::endl; + if (FFMS_SetOutputFormatV2(source, pixfmts, propframe->EncodedWidth, propframe->EncodedHeight, FFMS_RESIZER_BICUBIC, &err)) { + std::cerr<<"ffmpegsource: "<<err.Buffer<<std::endl; mutex.unlock(); return false; } @@ -608,6 +608,44 @@ bool libav::ffms2_decoder::open(const std::string& filename){ return loaded; } +bool libav::audio_decoder::open(const std::string& filename){ + mutex.lock(); + 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; + mutex.unlock(); + 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; + mutex.unlock(); + 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; + mutex.unlock(); + return false; + } + FFMS_DestroyIndex(index); + props = FFMS_GetAudioProperties(source); + + std::cerr<<"ffmpegsource: successfully opened "<<filename<<std::endl; + loaded=true; + mutex.unlock(); + return loaded; +} + void libav::audio_decoder::cleanup(){ + if (loaded) { + mutex.lock(); + FFMS_DestroyAudioSource(source); + mutex.unlock(); + loaded=false; + } +} /////////////////////////// // encoder methods // /////////////////////////// diff --git a/rotord/src/libavwrapper.h b/rotord/src/libavwrapper.h index 870815f..2f1f31e 100644 --- a/rotord/src/libavwrapper.h +++ b/rotord/src/libavwrapper.h @@ -156,68 +156,129 @@ namespace libav { }; - class ffms2_decoder + class video_decoder { public: - ffms2_decoder(){ + video_decoder(){ maybeInitFFMpegLib(); pixfmts[0] = FFMS_GetPixFmt("rgb24"); pixfmts[1] = -1; h=0; w=0; - videosource=NULL; + source=NULL; loaded=false; - errinfo.Buffer = errmsg; - errinfo.BufferSize = sizeof(errmsg); - errinfo.ErrorType = FFMS_ERROR_SUCCESS; - errinfo.SubType = FFMS_ERROR_SUCCESS; + err.Buffer = errmsg; + err.BufferSize = sizeof(errmsg); + err.ErrorType = FFMS_ERROR_SUCCESS; + err.SubType = FFMS_ERROR_SUCCESS; } - ~ffms2_decoder(){ + ~video_decoder(){ cleanup(); } void cleanup(); bool open(const std::string& filename); - float getFrameRate(){ - if (loaded) return (((float)videoprops->FPSNumerator)/((float)videoprops->FPSDenominator)); + float get_framerate(){ + if (loaded) return (((float)props->FPSNumerator)/((float)props->FPSDenominator)); else return -1.0f; } - int getNumberOfFrames(){ - if (loaded) return videoprops->NumFrames; + int get_number_frames(){ + if (loaded) return props->NumFrames; else return -1; } - int getNumberOfChannels(){ + int get_number_channels(){ return 3; //this is what we convert to } - int getWidth(){ + int get_width(){ return w; } - int getHeight(){ + int get_height(){ return h; } - bool fetchFrame(int width,int height,int wanted){ - if (FFMS_SetOutputFormatV2(videosource, pixfmts, width, height, FFMS_RESIZER_BICUBIC, &errinfo)) { - std::cerr<<"ffmpegsource: "<<errinfo.Buffer<<std::endl; + bool fetch_frame(int width,int height,int wanted){ + if (FFMS_SetOutputFormatV2(source, pixfmts, width, height, FFMS_RESIZER_BICUBIC, &err)) { + std::cerr<<"ffmpegsource: "<<err.Buffer<<std::endl; return false; } - frame = FFMS_GetFrame(videosource, wanted%videoprops->NumFrames, &errinfo); + frame = FFMS_GetFrame(source, wanted%props->NumFrames, &err); if (frame == NULL) { - std::cerr<<"ffmpegsource: "<<errinfo.Buffer<<std::endl; + std::cerr<<"ffmpegsource: "<<err.Buffer<<std::endl; return false; } return true; } - FFMS_VideoSource *videosource; - FFMS_VideoProperties *videoprops; + FFMS_VideoSource *source; + FFMS_VideoProperties *props; FFMS_Frame *frame; - FFMS_ErrorInfo errinfo; + FFMS_ErrorInfo err; char errmsg[1024]; int pixfmts[2]; bool loaded; int h,w; }; + class audio_decoder + { + public: + audio_decoder(){ + maybeInitFFMpegLib(); + source=nullptr; + props=nullptr; + loaded=false; + err.Buffer = errmsg; + err.BufferSize = sizeof(errmsg); + err.ErrorType = FFMS_ERROR_SUCCESS; + err.SubType = FFMS_ERROR_SUCCESS; + } + ~audio_decoder(){ + cleanup(); + } + void cleanup(); + bool open(const std::string& filename); + int get_format(){ + if (props) return props->SampleFormat; + else return 0; + } + int get_sample_rate(){ + if (props) return props->SampleRate; + else return 0; + } + int get_bit_depth(){ + if (props) return props->BitsPerSample; + else return 0; + } + int get_number_channels(){ + if (props) return props->Channels; + else return 0; + } + int64_t get_channel_layout(){ + if (props) return props->ChannelLayout; + else return 0; + } + float get_duration(){ + if (props) return ((float)props->NumSamples)/props->SampleRate; + else return 0; + } + bool get_samples(void *buf,int64_t start, int64_t count){ + if (source) { + if (FFMS_GetAudio(source, buf, start, count, &err)) { + std::cerr<<"ffmpegsource: "<<err.Buffer<<std::endl; + return false; + } + return true; + } + return false; + } + + FFMS_AudioSource *source; + FFMS_AudioProperties *props; + FFMS_Frame *frame; + FFMS_ErrorInfo err; + char errmsg[1024]; + bool loaded; + }; + /* // TODO - finish refactoring based on // http://svn.gnumonks.org/trunk/21c3-video/ffmpeg/ffmpeg-0.4.9-pre1/output_example.c diff --git a/rotord/src/rotor.h b/rotord/src/rotor.h index ff8675a..447ba51 100644 --- a/rotord/src/rotor.h +++ b/rotord/src/rotor.h @@ -933,10 +933,10 @@ namespace Rotor { isLoaded=player.open(filename); if (isLoaded){ logger.information("Video_loader loaded "+filename+": "\ - +toString(player.getNumberOfFrames())+" frames, "\ - +toString(player.getFrameRate())+" fps, "\ - +toString(player.getWidth())+"x"+toString(player.getHeight())\ - +", channels:"+toString(player.getNumberOfChannels())); + +toString(player.get_number_frames())+" frames, "\ + +toString(player.get_framerate())+" fps, "\ + +toString(player.get_width())+"x"+toString(player.get_height())\ + +", channels:"+toString(player.get_number_channels())); return true; } logger.error("Video_loader failed to load "+filename); @@ -944,11 +944,11 @@ namespace Rotor { } Image *output(const Frame_spec &frame){ if (isLoaded){ - float clipframerate=(parameters["framerate"]->value==0.0f?player.getFrameRate():parameters["framerate"]->value); + float clipframerate=(parameters["framerate"]->value==0.0f?player.get_framerate():parameters["framerate"]->value); float clipspeed=(clipframerate/frame.framerate)*parameters["speed"]->value; - int wanted=(((int) ((frame.time*frame.framerate)+0.5))%max(1,player.getNumberOfFrames()-1)); + int wanted=(((int) ((frame.time*frame.framerate)+0.5))%max(1,player.get_number_frames()-1)); if (wanted!=lastframe){ - if (!player.fetchFrame(frame.w,frame.h,wanted)) { //seek fail + if (!player.fetch_frame(frame.w,frame.h,wanted)) { //seek fail Poco::Logger& logger = Poco::Logger::get("Rotor"); logger.error("Video_loader failed to seek frame "+toString(wanted)+" of "+attributes["filename"]->value); @@ -966,7 +966,7 @@ namespace Rotor { bool isLoaded; private: //ffmpegsource::decoder player; - libav::ffms2_decoder player; + libav::video_decoder player; int lastframe; }; class Video_output: public Image_node { |
