📄 capproc.cc
字号:
#include "capproc.h"#include "dsp.h"#include <unistd.h>#include <stdio.h>#include <avifile.h>#include <videoencoder.h>#include <iostream># include <avm_fourcc.h># include <utils.h># include <cpuinfo.h># include <creators.h># include <except.h>using namespace Creators;using namespace std;#define __MODULE__ "Capture Process"/* Class : frame_allocator * Description: Manages a stack of frames. It gives out pointers to * : frames that can be written to. * Parameters : None. * Returns : Nothing. * SideEffects: None. */class frame_allocator{ struct frame { char* data; /* frame number (int, first 4 bytes) + frame data */ int status; /* is this frame available for capturing? */ }; int _w; int _h; vector<frame> frames; unsigned int _limit; int refs;public: frame_allocator(int w, int h, int limit):_w(w), _h(h), _limit(limit),refs(2){} /* Function : release * Description: No clue. But if you call it three times in a row, it will * : destruct this instance. Before that, it will just decrease * : a reference counter. * Parameters : None. * Returns : Nothing. * SideEffects: None. */ void release() { refs--; if (!refs) { delete this; } } /* Function : alloc * Description: Returns a pointer to a memory area to write framedata to. * Parameters : None. * Returns : Nothing. * SideEffects: None. */ char* alloc() { unsigned int i; /* possibly there's an unused frame (status == 0) to store the data in. * If so, find it and return a pointer to where the captured data can * be stored. */ for(i=0; i<frames.size(); i++) { if(frames[i].status==0) { frames[i].status=1; *(int*)(frames[i].data)=i; return frames[i].data+4; } } /* no unused frame found. Create a new one and push it on the frames * stack */ if(frames.size()>=_limit)return 0; frame f; /* 3 == 3 bytes for each pixel. Blerh. 4 == framenumer. Blergh++. */ f.data=new char[_w*_h*3+4]; f.status=1; frames.push_back(f); *(int*)(f.data)=i; return f.data+4; } /* Function : free * Description: Marks a frame unused by settings its status flag to 0 * Parameters : mem * : Pointer to frame data (+4), as given out by * : this->alloc() before. * Returns : Nothing. * SideEffects: None. */ void free(char* mem) { if(!mem) { /* really should throw an exception here. */ cerr<<"ERROR: Freeing 0!"<<endl; return; } int id=*(int*)(mem-4); /* is this really a pointer within a valid frame ? */ if((id<0)||((unsigned)id>=frames.size())||(frames[id].data!=(mem-4))) { cerr<<"ERROR: Freeing unknown memory!"<<endl; return; } if(frames[id].status==0) { /* it wasn't even used. */ cerr<<"ERROR: Duplicate free()!"<<endl; return; } frames[id].status=0; } ~frame_allocator() { for(unsigned int i=0; i<frames.size(); i++) delete frames[i].data; }};static frame_allocator* _allocator=0;void* CaptureProcess::vidcap(void* arg){ CaptureProcess& a=*(CaptureProcess*)arg; int w=a.res_w; int h=a.res_h; const float fps=a.fps; debug("Starting video capture thread.");// char tmpframe[384*288*4]; a.m_v4l->grabSetParams(1, &w, &h, VIDEO_PALETTE_RGB24); debug("Set capture parameters."); long long& inittime=a.starttime; int& cnt=a.cnt; int& drop=a.cap_drop; cnt=0; drop=0; /* reserve space for 50 unencoded frames */ _allocator=new frame_allocator(w,h,50); /* start the capture loop until something makes us quit */ while(!a.m_quit) { long long currenttime=longcount(); char* z=0;// cerr<<currenttime<<" "<<inittime<<" "<<fps<<std::endl;// cerr<<to_float(currenttime, inittime)*fps<<" "<<cnt<<std::endl;// double freq=550000.; /* Are we ready for the next frame yet? */ double dist=double(currenttime-inittime)/(freq*1000.); if(dist*fps<cnt) { /* No, the current frame is still out there.. let's take a short nap. */ avm_usleep(10000); continue; } chunk ch; /* Did we wait too long and are we too late for our new frame? */ if(dist*fps<(cnt+1)) { /* Nope, we're in time. Capture the mofo. */ z=a.m_v4l->grabCapture(false); char* tmpframe=_allocator->alloc(); if(tmpframe) { // o_* original_*; c_* capture_* int c_lines = a.window.height; int c_bpl = a.window.width * 3; int o_bpl = w * 3; int o_y_end = a.window.y + a.window.height; int o_x_off = a.window.x * 3; char *last_line_buffer = NULL; if (a.vertical_flip) { /* pointer to last line in buffer. Used ofte in line loop */ last_line_buffer = tmpframe + (c_lines - 1) * c_bpl; } for (int i = 0; i < c_lines; i++) { char *line_buffer; if (a.vertical_flip) line_buffer = last_line_buffer - (i * c_bpl); else line_buffer = tmpframe + (i * c_bpl); memcpy(line_buffer, z + (o_y_end -i -1)*o_bpl + o_x_off, c_bpl); } } ch.data=tmpframe; } else { ch.data=0; drop++; } ch.timestamp=dist; a.m_vidq.push(ch); cnt++; //if(cnt%100==0) // cerr<<"Capture thread: read "<<cnt<<" frames, dropped "<<drop<<" frames"<<std::endl; } _allocator->release(); cerr<<"Capture thread exiting"<<endl; return 0;}int audioblock=0;void* CaptureProcess::audcap(void* arg){ CaptureProcess& a=*(CaptureProcess*)arg; cerr << "Opening dsp...." << endl; dsp* thedsp=new dsp(a.sound_dev); cerr << "Dsp opened!" << endl; if ( thedsp->opendev(a.samplesize,a.chan,a.frequency) ==0 ) { //returns file descriptor return 0; } cerr << "Dsp configuration set." << endl; float abps = a.samplesize * a.chan * a.frequency / 8; // #bytes / second char* buf = NULL; int bufsize = 0; int blocksize = thedsp->getBufSize(); int tim = 0; int counter = 0; audioblock = blocksize; while(!a.m_quit) { buf = new char[audioblock]; memcpy(buf, thedsp->readBuf(), audioblock); long long ct = longcount(); double timestamp_shift = (audioblock + thedsp->getSize()) / abps; if (!(counter%10)) cerr << "Timeshift: " << timestamp_shift << ", getSize(): " << thedsp->getSize() << endl; chunk ch; ch.data=buf; if(a.starttime) { ch.timestamp = (double(ct - a.starttime) / (freq * 1000.)) - timestamp_shift; } else { ch.timestamp=-1; } a.m_audq.push(ch); bufsize+=blocksize; tim++; if ( blocksize / abps > .1 ) { avm_usleep(10000); } counter++; } delete thedsp; return 0;}void* CaptureProcess::writer(void* arg){ CaptureProcess& a=*(CaptureProcess*)arg; IAviWriteFile* file=0; IAviSegWriteFile* sfile=0; debug("Starting writer thread."); IAviVideoWriteStream* stream; IAviWriteStream* audioStream=0; IAviAudioWriteStream *mp3Stream = 0; BitmapInfo bh(a.window.width, a.window.height, 24); const double fps=a.fps; if(fps==0) { throw FATAL("FPS = 0 !"); } try { debug("Creating AVI file.."); if(a.segment_size==-1) file=CreateIAviWriteFile(a.filename.c_str()); else { sfile=CreateSegmentedFile(a.filename.c_str(), a.segment_size); file=sfile; } // FILE* zz=fopen("bin/uncompr.bmp", "rb"); debug("Adding video stream."); stream=file->AddVideoStream(a.compressor, &bh, int(1000000./a.fps)); debug("Done."); // stream=file->AddStream(AviStream::Video); // ve.Init(fccIV50, (const char*)&bh); } catch(FatalError& e) { e.Print(); a.m_quit=1; return 0; } float abps=(a.samplesize*a.frequency*a.chan)/8; WAVEFORMATEX wfm; wfm.wFormatTag=1;//PCM wfm.nChannels=a.chan; wfm.nSamplesPerSec=a.frequency; wfm.nAvgBytesPerSec=(int)abps; wfm.nBlockAlign=(a.samplesize*a.chan)/8; wfm.wBitsPerSample=a.samplesize; wfm.cbSize=0; if(audioStream==0 && mp3Stream == 0) { if (a.audiocodec) { debug("Adding mp3 audio stream."); mp3Stream = file->AddAudioStream( a.audiocodec, &wfm, a.audiobitrate); mp3Stream->Start(); debug("Done."); } else { debug("Adding uncompressed audio stream."); audioStream=file->AddStream(AviStream::Audio, (const char*)&wfm, 18, 1, //uncompressed PCM data (int)abps, //bytes/sec (a.samplesize*a.chan)/8 //bytes/sample ); debug("Done."); } } //ve.SetQuality(9500); //ve.Start(); stream->SetQuality(a.quality); stream->Start(); cerr<<"Entering loop"<<endl; //BITMAPINFOHEADER obh=ve.QueryOutputFormat(); //stream->SetFormat((const char*)&obh, sizeof obh); int cnt=0; int& drop=a.comp_drop; long long audiodata=0LL; int videodata=0; double video_error=0; int hide_video=0; int dup_video=0; int finished = 0; double snd_time = 0; double vid_time = 0; drop=0; while(!finished) { chunk ch; int finished = 0; while(a.m_vidq.size()>50) // skip frames if queue is too full { ch=a.m_vidq.front(); a.m_vidq.pop(); vid_time=ch.timestamp; cnt++; if(ch.data)//delete ch.data; _allocator->free(ch.data); stream->AddFrame(0); videodata++; drop++; } while((a.m_vidq.size()==0) && (a.m_audq.size()==0)) // ok empty { if(a.m_quit) { finished = 1; break; } avm_usleep(10000); } if (finished) break; if(a.m_vidq.size()) // something to do { ch=a.m_vidq.front(); a.m_vidq.pop(); vid_time=ch.timestamp; cnt++; if(!hide_video) { videodata++; CImage* im=0; if(ch.data) im=new CImage(&bh, (unsigned char*)ch.data, false); stream->AddFrame(im); if(dup_video) { videodata++; stream->AddFrame(im); video_error+=1./fps; } if(im) im->Release(); } else video_error-=1./fps; dup_video=hide_video=0; if(ch.data) _allocator->free(ch.data); } if(a.m_audq.size()) { ch=a.m_audq.front(); a.m_audq.pop(); //std::cerr<<ch.timestamp-snd_time<<" "<<ch.timestamp-(audiodata+audioblock)/44100./2<<std::endl; snd_time=ch.timestamp; if (a.audiocodec) { mp3Stream->AddData(ch.data, audioblock); } else { audioStream->AddChunk(ch.data, audioblock, AviStream::KEYFRAME); } audiodata+=audioblock; double audio_error=audiodata/abps-ch.timestamp; if(audio_error<video_error-5./fps) { hide_video=1; } if(audio_error>video_error+5./fps) { dup_video=1; } delete ch.data; } if(a.segment_flag && sfile) { sfile->Segment(); a.segment_flag=0; //vid_clear=aud_clear=0; } if(a.timelimit!=-1) { if(snd_time>a.timelimit) { a.m_quit=1; } if(vid_time>a.timelimit) { a.m_quit=1; } } if(a.sizelimit!=-1) { if(file->GetFileSize()>a.sizelimit) { a.m_quit=1; } } } long long size=file->GetFileSize(); delete file; _allocator->release(); cerr<<"Write thread exiting"<<endl; (*a.messenger)<<"Audio: written "<<(long int)audiodata<<" bytes ( "<<(long int)(audiodata/(44100.*2))<<" s )."<<endl; (*a.messenger)<<"Video: written "<<videodata<<" frames ( "<<videodata/fps<<" s )."<<endl; (*a.messenger)<<"End video pos "<<vid_time<<" s, end audio pos "<<snd_time<<" s."<<endl; (*a.messenger)<<"File size: "<<(double)(size/1000)<<" Kb ( "<<(double)(size/1000/vid_time)<<" Kb/s )."<<endl; (*a.messenger)<<"Synch fix: "<<videodata-cnt<<" frames."<<endl; (*a.messenger)<<"Frame drop: "<<100.*double(a.cap_drop+drop)/videodata<<"%."<<endl<<flush; (*a.messenger)<<ends; return 0;}void CaptureProcess::Create(v4lxif* v4l, string filename, int segment_size, int compressor, int quality, int keyframes, enum Sound_Freqs frequency, enum Sample_Sizes samplesize, enum Sound_Chans chan, enum Resolutions res, int timelimit, int sizelimit, float fps, window_t window, int audiocodec, int audiobitrate, int vertical_flip, const char *sound_dev){ m_v4l=v4l; m_quit=0; this->cap_drop = 0; starttime=longcount(); this->filename=filename; this->segment_size=segment_size; this->compressor=compressor; this->quality=quality; this->keyframes=keyframes; this->audiocodec = audiocodec; this->audiobitrate = audiobitrate; this->vertical_flip = vertical_flip; this->sound_dev = sound_dev; //TODO: debug stream cerr << "Audiocodec: " << audiocodec << ", bitrate: " << audiobitrate << endl; switch(frequency) { case NoAudio: break; case F44: this->frequency=44100; break; case F22: this->frequency=22050; break; case F11: this->frequency=11025; break; default: throw FATAL("Unknown frequency"); } if(frequency!=NoAudio) { switch(samplesize) { case S16: this->samplesize=16; break; case S8: this->samplesize=8; break; default: throw FATAL("Unknown audio sample size"); } switch(chan) { case Mono: this->chan=1; break; case Stereo: this->chan=2; break; default: throw FATAL("Unknown channel number"); } } int i = 0; while (restable[i].res != res && restable[i].res != WNONE) { i++; } if (restable[i].res == res) { res_w = restable[i].width; res_h = restable[i].height; } else { throw FATAL("Unknown video resolution"); } this->timelimit=timelimit; this->sizelimit=sizelimit; this->fps=fps; this->window = window; printf ("window: %i %i %i %i \n", window.x, window.width, window.y, window.height); pthread_create(&m_vidc, 0, CaptureProcess::vidcap, this); if(frequency!=NoAudio) { pthread_create(&m_audc, 0, CaptureProcess::audcap, this); } pthread_create(&m_writer, 0, CaptureProcess::writer, this);}CaptureProcess::~CaptureProcess(){ m_quit=1; cerr << "Waiting for write thread" << endl;; pthread_join(m_writer,0); cerr << "Waiting for audio thread" << endl;; pthread_join(m_audc,0); cerr << "Waiting for video thread" << endl;; pthread_join(m_vidc,0); cerr<<"All threads exited"<<endl; while(m_audq.size()) { chunk z=m_audq.front(); m_audq.pop(); if(z.data)delete z.data; } }// vim:ts=2:sw=2
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -