📄 ffgrab.cpp
字号:
startDecodingAt = 0;
for (map<unsigned int,double>::const_iterator it=keyframes.begin();it != keyframes.end();it++)
{
if (it->first <= minFrame && it->first > startDecodingAt) startDecodingAt = it->first;
if (DEBUG) FFprintf("%d %d\n",it->first,startDecodingAt);
}
}
// the meaning of frames doesn't make much sense for audio...
}
void FFGrabber::setTime(double startTime, double stopTime)
{
this->startTime = startTime;
this->stopTime = stopTime;
frameNrs.clear();
for (int i=0; i < videos.size(); i++)
{
Grabber* CB = videos.at(i);
if (CB)
{
CB->frames.clear();
CB->frameNrs.clear();
CB->frameNr = 0;
CB->packetNr = 0;
CB->startTime = startTime;
CB->stopTime = stopTime;
}
}
for (int i=0; i < audios.size(); i++)
{
Grabber* CB = audios.at(i);
if (CB)
{
CB->frames.clear();
CB->frameNrs.clear();
CB->startTime = startTime;
CB->stopTime = stopTime;
}
}
}
#ifdef MATLAB_MEX_FILE
void FFGrabber::setMatlabCommand(char * matlabCommand)
{
this->matlabCommand = matlabCommand;
}
void FFGrabber::runMatlabCommand(Grabber* G)
{
if (matlabCommand)
{
mwSize dims[2];
dims[1]=1;
int width=G->info.video.width, height = G->info.video.height;
mxArray* plhs[] = {NULL};
int ExitCode;
mexSetTrapFlag(0);
if (G->frames.size() == 0) return;
vector<uint8_t*>::iterator lastframe = --(G->frames.end());
if (*lastframe == NULL) return;
dims[0] = G->frameBytes.back();
if (prhs[0] == NULL)
{
//make matrices to pass to the matlab function
prhs[0] = mxCreateNumericArray(2, dims, mxUINT8_CLASS, mxREAL); // empty 2d matrix
prhs[1] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(prhs[1])[0] = width;
prhs[2] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(prhs[2])[0] = height;
prhs[3] = mxCreateDoubleMatrix(1,1,mxREAL);
prhs[4] = mxCreateDoubleMatrix(1,1,mxREAL);
}
mxGetPr(prhs[3])[0] = G->frameNrs.size()==0?G->frameTimes.size():G->frameNrs[G->frameTimes.size()-1];
mxGetPr(prhs[4])[0] = G->frameTimes.back();
memcpy(mxGetPr(prhs[0]),*lastframe,dims[0]);
//free the frame memory
free(*lastframe);
*lastframe = NULL;
//call Matlab
ExitCode = mexCallMATLAB(0,plhs,5,prhs,matlabCommand);
}
}
#endif
int FFGrabber::build(char* filename, bool disableVideo, bool disableAudio, bool tryseeking)
{
if (DEBUG) FFprintf("avbin_open_filename\n");
file = avbin_open_filename(filename);
if (!file) return -4;
//detect if the file has changed
struct stat fstat;
stat(filename,&fstat);
if (!this->filename || strcmp(this->filename,filename)!=0 || filestat.st_mtime != fstat.st_mtime)
{
free(this->filename);
this->filename=strdup(filename);
memcpy(&filestat,&fstat,sizeof(fstat));
keyframes.clear();
startDecodingAt = 0xFFFFFFFF;
}
fileinfo.structure_size = sizeof(fileinfo);
if (DEBUG) FFprintf("avbin_file_info\n");
if (avbin_file_info(file, &fileinfo)) return -1;
for (int stream_index=0; stream_index<fileinfo.n_streams; stream_index++)
{
AVbinStreamInfo streaminfo;
streaminfo.structure_size = sizeof(streaminfo);
if (DEBUG) FFprintf("avbin_stream_info\n");
avbin_stream_info(file, stream_index, &streaminfo);
if (DEBUG) FFprintf("%lld\n",streaminfo,fileinfo.start_time);
if (avbin_get_version() < 8)
{
AVRational frame_rate = file->context->streams[stream_index]->r_frame_rate; streaminfo.video.frame_rate_num = frame_rate.num; streaminfo.video.frame_rate_den = frame_rate.den; }
if (streaminfo.type == AVBIN_STREAM_TYPE_VIDEO && !disableVideo)
{
AVbinStream * tmp = avbin_open_stream(file, stream_index);
if (tmp)
{
double rate = streaminfo.video.frame_rate_num/(0.00001+streaminfo.video.frame_rate_den);
streams[stream_index]=new Grabber(false,tmp,tryseeking,rate,streaminfo.video.height*streaminfo.video.width*3,streaminfo,fileinfo.start_time);
videos.push_back(streams[stream_index]);
} else {
FFprintf("Could not open video stream\n");
}
}
if (streaminfo.type == AVBIN_STREAM_TYPE_AUDIO && !disableAudio)
{
AVbinStream * tmp = avbin_open_stream(file, stream_index);
if (tmp)
{
streams[stream_index]=new Grabber(true,tmp,tryseeking,streaminfo.audio.sample_rate,streaminfo.audio.sample_bits*streaminfo.audio.channels,streaminfo,fileinfo.start_time);
audios.push_back(streams[stream_index]);
} else {
FFprintf("Could not open audio stream\n");
}
}
}
this->tryseeking = tryseeking;
stopForced = false;
return 0;
}
int FFGrabber::doCapture()
{
AVbinPacket packet;
packet.structure_size = sizeof(packet);
streammap::iterator tmp;
int needseek=1;
bool allDone = false;
while (!avbin_read(file, &packet))
{
if ((tmp = streams.find(packet.stream_index)) != streams.end())
{
Grabber* G = tmp->second;
G->Grab(&packet);
if (G->done)
{
allDone = true;
for (streammap::iterator i = streams.begin(); i != streams.end() && allDone; i++)
{
allDone = allDone && i->second->done;
}
}
#ifdef MATLAB_MEX_FILE
if (!G->isAudio) runMatlabCommand(G);
#endif
}
if (tryseeking && needseek)
{
if (stopTime && startTime > 0) {
if (DEBUG) FFprintf("try seeking to %lf\n",startTime);
av_seek_frame(file->context, -1, (AVbinTimestamp)(startTime*1000*1000), AVSEEK_FLAG_BACKWARD);
}
needseek = 0;
}
if (allDone)
{
if (DEBUG) FFprintf("stopForced\n");
stopForced = true;
break;
}
}
#ifdef MATLAB_MEX_FILE
if (prhs[0])
{
mxDestroyArray(prhs[0]);
if (prhs[1]) mxDestroyArray(prhs[1]);
if (prhs[2]) mxDestroyArray(prhs[2]);
if (prhs[3]) mxDestroyArray(prhs[3]);
if (prhs[4]) mxDestroyArray(prhs[4]);
}
prhs[0] = NULL;
#endif
return 0;
}
#ifdef MATLAB_MEX_FILE
FFGrabber FFG;
char* message(int err)
{
switch (err)
{
case 0: return "";
case -1: return "Unable to initialize";
case -2: return "Invalid interface";
case -4: return "Unable to open file";
case -5: return "AVbin version 8 or greater is required!";
default: return "Unknown error";
}
}
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
if (nrhs < 1 || !mxIsChar(prhs[0])) mexErrMsgTxt("First parameter must be the command (a string)");
char cmd[100];
mxGetString(prhs[0],cmd,100);
if (!strcmp("build",cmd))
{
if (nrhs < 5 || !mxIsChar(prhs[1])) mexErrMsgTxt("build: parameters must be the filename (as a string), disableVideo, disableAudio, trySeeking");
if (nlhs > 0) mexErrMsgTxt("build: there are no outputs");
int filenamelen = mxGetN(prhs[1])+1;
char* filename = new char[filenamelen];
if (!filename) mexErrMsgTxt("build: out of memory");
mxGetString(prhs[1],filename,filenamelen);
char* errmsg = message(FFG.build(filename, mxGetScalar(prhs[2]), mxGetScalar(prhs[3]), mxGetScalar(prhs[4])));
delete[] filename;
if (strcmp("",errmsg)) mexErrMsgTxt(errmsg);
} else if (!strcmp("doCapture",cmd)) {
if (nlhs > 0) mexErrMsgTxt("doCapture: there are no outputs");
char* errmsg = message(FFG.doCapture());
if (strcmp("",errmsg)) mexErrMsgTxt(errmsg);
} else if (!strcmp("getVideoInfo",cmd)) {
if (nrhs < 2 || !mxIsNumeric(prhs[1])) mexErrMsgTxt("getVideoInfo: second parameter must be the video stream id (as a number)");
if (nlhs > 6) mexErrMsgTxt("getVideoInfo: there are only 5 output values: width, height, rate, nrFramesCaptured, nrFramesTotal");
unsigned int id = (unsigned int)mxGetScalar(prhs[1]);
int width,height,nrFramesCaptured,nrFramesTotal;
double rate, totalDuration;
char* errmsg = message(FFG.getVideoInfo(id, &width, &height,&rate, &nrFramesCaptured, &nrFramesTotal, &totalDuration));
if (strcmp("",errmsg)) mexErrMsgTxt(errmsg);
if (nlhs >= 1) {plhs[0] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(plhs[0])[0] = width; }
if (nlhs >= 2) {plhs[1] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(plhs[1])[0] = height; }
if (nlhs >= 3) {plhs[2] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(plhs[2])[0] = rate; }
if (nlhs >= 4) {plhs[3] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(plhs[3])[0] = nrFramesCaptured; }
if (nlhs >= 5) {plhs[4] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(plhs[4])[0] = nrFramesTotal; }
if (nlhs >= 6) {plhs[5] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(plhs[5])[0] = totalDuration; }
} else if (!strcmp("getAudioInfo",cmd)) {
if (nrhs < 2 || !mxIsNumeric(prhs[1])) mexErrMsgTxt("getAudioInfo: second parameter must be the audio stream id (as a number)");
if (nlhs > 7) mexErrMsgTxt("getAudioInfo: there are only 6 output values: nrChannels, rate, bits, nrFramesCaptured, nrFramesTotal, subtype");
unsigned int id = (unsigned int)mxGetScalar(prhs[1]);
int nrChannels,bits,nrFramesCaptured,nrFramesTotal,subtype;
double rate, totalDuration;
char* errmsg = message(FFG.getAudioInfo(id, &nrChannels, &rate, &bits, &nrFramesCaptured, &nrFramesTotal, &subtype, &totalDuration));
if (strcmp("",errmsg)) mexErrMsgTxt(errmsg);
if (nlhs >= 1) {plhs[0] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(plhs[0])[0] = nrChannels; }
if (nlhs >= 2) {plhs[1] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(plhs[1])[0] = rate; }
if (nlhs >= 3) {plhs[2] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(plhs[2])[0] = bits; }
if (nlhs >= 4) {plhs[3] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(plhs[3])[0] = nrFramesCaptured; }
if (nlhs >= 5) {plhs[4] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(plhs[4])[0] = nrFramesTotal; }
if (nlhs >= 6) {plhs[5] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(plhs[5])[0] = subtype==AVBIN_SAMPLE_FORMAT_FLOAT?1:0; }
if (nlhs >= 7) {plhs[6] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(plhs[6])[0] = totalDuration; }
} else if (!strcmp("getCaptureInfo",cmd)) {
if (nlhs > 2) mexErrMsgTxt("getCaptureInfo: there are only 2 output values: nrVideo, nrAudio");
int nrVideo, nrAudio;
FFG.getCaptureInfo(&nrVideo, &nrAudio);
if (nlhs >= 1) {plhs[0] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(plhs[0])[0] = nrVideo; }
if (nlhs >= 2) {plhs[1] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(plhs[1])[0] = nrAudio; }
} else if (!strcmp("getVideoFrame",cmd)) {
if (nrhs < 3 || !mxIsNumeric(prhs[1]) || !mxIsNumeric(prhs[2])) mexErrMsgTxt("getVideoFrame: second parameter must be the audio stream id (as a number) and third parameter must be the frame number");
if (nlhs > 2) mexErrMsgTxt("getVideoFrame: there are only 2 output value: data");
unsigned int id = (unsigned int)mxGetScalar(prhs[1]);
unsigned int frameNr = (unsigned int)mxGetScalar(prhs[2]);
uint8_t* data;
unsigned int nrBytes;
double time;
mwSize dims[2];
dims[1]=1;
char* errmsg = message(FFG.getVideoFrame(id, frameNr, &data, &nrBytes, &time));
if (strcmp("",errmsg)) mexErrMsgTxt(errmsg);
dims[0] = nrBytes;
plhs[0] = mxCreateNumericArray(2, dims, mxUINT8_CLASS, mxREAL); // empty 2d matrix
memcpy(mxGetPr(plhs[0]),data,nrBytes);
free(data);
if (nlhs >= 2) {plhs[1] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(plhs[1])[0] = time; }
} else if (!strcmp("getAudioFrame",cmd)) {
if (nrhs < 3 || !mxIsNumeric(prhs[1]) || !mxIsNumeric(prhs[2])) mexErrMsgTxt("getAudioFrame: second parameter must be the audio stream id (as a number) and third parameter must be the frame number");
if (nlhs > 2) mexErrMsgTxt("getAudioFrame: there are only 2 output value: data");
unsigned int id = (unsigned int)mxGetScalar(prhs[1]);
unsigned int frameNr = (unsigned int)mxGetScalar(prhs[2]);
uint8_t* data;
unsigned int nrBytes;
double time;
mwSize dims[2];
dims[1]=1;
mxClassID mxClass;
char* errmsg = message(FFG.getAudioFrame(id, frameNr, &data, &nrBytes, &time));
if (strcmp("",errmsg)) mexErrMsgTxt(errmsg);
int nrChannels,bits,nrFramesCaptured,nrFramesTotal,subtype;
double rate, totalDuration;
FFG.getAudioInfo(id, &nrChannels, &rate, &bits, &nrFramesCaptured, &nrFramesTotal, &subtype, &totalDuration);
switch (bits)
{
case 8:
{
dims[0] = nrBytes;
mxClass = mxUINT8_CLASS;
break;
}
case 16:
{
mxClass = mxINT16_CLASS;
dims[0] = nrBytes/2;
break;
}
case 24:
{
int* tmpdata = (int*)malloc(nrBytes/3*4);
int i;
//I don't know how 24bit float data is organized...
for (i=0;i<nrBytes/3;i++)
{
tmpdata[i] = (((0x80&data[i*3+2])?-1:0)&0xFF000000) | ((data[i*3+2]<<16)+(data[i*3+1]<<8)+data[i*3]);
}
free(data);
data = (uint8_t*)tmpdata;
mxClass = mxINT32_CLASS;
dims[0] = nrBytes/3;
nrBytes = nrBytes/3*4;
break;
}
case 32:
{
mxClass = subtype==AVBIN_SAMPLE_FORMAT_S32?mxINT32_CLASS:subtype==AVBIN_SAMPLE_FORMAT_FLOAT?mxSINGLE_CLASS:mxUINT32_CLASS;
dims[0] = nrBytes/4;
break;
}
default:
{
dims[0] = nrBytes;
mxClass = mxUINT8_CLASS;
break;
}
}
plhs[0] = mxCreateNumericArray(2, dims, mxClass, mxREAL); // empty 2d matrix
memcpy(mxGetPr(plhs[0]),data,nrBytes);
free(data);
if (nlhs >= 2) {plhs[1] = mxCreateDoubleMatrix(1,1,mxREAL); mxGetPr(plhs[1])[0] = time; }
} else if (!strcmp("setFrames",cmd)) {
if (nrhs < 2 || !mxIsDouble(prhs[1])) mexErrMsgTxt("setFrames: second parameter must be the frame numbers (as doubles)");
if (nlhs > 0) mexErrMsgTxt("setFrames: has no outputs");
int nrFrames = mxGetN(prhs[1]) * mxGetM(prhs[1]);
unsigned int* frameNrs = new unsigned int[nrFrames];
if (!frameNrs) mexErrMsgTxt("setFrames: out of memory");
double* data = mxGetPr(prhs[1]);
for (int i=0; i<nrFrames; i++) frameNrs[i] = (unsigned int)data[i];
FFG.setFrames(frameNrs, nrFrames);
delete[] frameNrs;
} else if (!strcmp("setTime",cmd)) {
if (nrhs < 3 || !mxIsDouble(prhs[1]) || !mxIsDouble(prhs[2])) mexErrMsgTxt("setTime: start and stop time are required (as doubles)");
if (nlhs > 0) mexErrMsgTxt("setTime: has no outputs");
FFG.setTime(mxGetScalar(prhs[1]), mxGetScalar(prhs[2]));
} else if (!strcmp("setMatlabCommand",cmd)) {
if (nrhs < 2 || !mxIsChar(prhs[1])) mexErrMsgTxt("setMatlabCommand: the command must be passed as a string");
if (nlhs > 0) mexErrMsgTxt("setMatlabCommand: has no outputs");
char * matlabCommand = (char*)calloc(100,1);
mxGetString(prhs[1],matlabCommand,100);
if (strlen(matlabCommand)==0)
{
FFG.setMatlabCommand(NULL);
free(matlabCommand);
} else FFG.setMatlabCommand(matlabCommand);
} else if (!strcmp("cleanUp",cmd)) {
if (nlhs > 0) mexErrMsgTxt("cleanUp: there are no outputs");
FFG.cleanUp();
}
}
#endif
#ifdef TEST_FFGRAB
int main(int argc, char** argv)
{
FFGrabber FFG;
printf("%s\n",argv[1]);
FFG.build(argv[1],false,false,true);
FFG.doCapture();
int nrVideo, nrAudio;
FFG.getCaptureInfo(&nrVideo, &nrAudio);
printf("there are %d video streams, and %d audio.\n",nrVideo,nrAudio);
}
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -