#include #include #include #include //#include #include #include #include #include #include #include #include #include #include #include #include "libcam_ffmpeg.h" #define CLEAR(x) memset (&(x), 0, sizeof (x)) /*STRUCTURES COPIED FROM FFMPEG HEADERS... i hope that it does not change !!!*/ /*************************************************************************/ camera_attributes cam_attributes_vector[] = { {V4L2_CID_BRIGHTNESS, "Brightness",0,0,0,0}, {V4L2_CID_CONTRAST,"Contrast",0,0,0,0}, {V4L2_CID_SATURATION,"Saturation",0,0,0,0}, {V4L2_CID_HUE,"Hue",0,0,0,0}, {V4L2_CID_AUDIO_VOLUME,"Audio Volume",0,0,0,0}, {V4L2_CID_AUDIO_BALANCE,"Audio Balance",0,0,0,0}, {V4L2_CID_AUDIO_BASS,"Audio Bass",0,0,0,0}, {V4L2_CID_AUDIO_TREBLE,"Audio Treble",0,0,0,0}, {V4L2_CID_AUDIO_MUTE,"Audio Mute",0,0,0,0}, {V4L2_CID_AUDIO_LOUDNESS,"Audio Loudness",0,0,0,0}, {V4L2_CID_BLACK_LEVEL,"Black Level",0,0,0,0}, {V4L2_CID_AUTO_WHITE_BALANCE,"Auto White Balance",0,0,0,0}, {V4L2_CID_DO_WHITE_BALANCE,"White Balance",0,0,0,0}, {V4L2_CID_RED_BALANCE,"Red Balance",0,0,0,0}, {V4L2_CID_BLUE_BALANCE,"Blue Balance",0,0,0,0}, {V4L2_CID_GAMMA,"Gamma",0,0,0,0}, {V4L2_CID_EXPOSURE,"Exposure",0,0,0,0}, {V4L2_CID_AUTOGAIN,"Autogain",0,0,0,0}, {V4L2_CID_GAIN ,"Gain",0,0,0,0}, {V4L2_CID_HFLIP,"HFlip",0,0,0,0}, {V4L2_CID_VFLIP,"VFlip",0,0,0,0}, {V4L2_CID_HCENTER,"HCenter",0,0,0,0}, {V4L2_CID_VCENTER,"VCenter",0,0,0,0}, {V4L2_CID_LASTP1,"LastP1",0,0,0,0} }; enum io_method { io_read, io_mmap, io_userptr }; struct video_data { int fd; int frame_format; /* V4L2_PIX_FMT_* */ enum io_method io_method; int width, height; int frame_rate; int frame_rate_base; int frame_size; int top_field_first; int buffers; void **buf_start; unsigned int *buf_len; }; /*************************************************************************/ struct ffmpeg_frame_t{ AVFrame* pFrame; uint8_t* data; int numBytes; }; struct camera_handler_t{ char* deviceName; AVFormatContext *pFormatCtx; AVInputFormat *iformat; AVFormatParameters formatParams; AVCodecContext *pCodecCtx; AVCodec *pCodec; ffmpegFrame bufferFrame; ffmpegFrame* capturedFrames; int numFrames,maxFrames; int videoStream; camera_attributes cam_attributes[NUM_CAM_ATTRIBUTES]; }; // IOCTL handling for V4L2 static int xioctl( int fd, int request, void *arg) { int r; do r = ioctl (fd, request, arg); while (-1 == r && EINTR == errno); return r; } void saveFrame(AVFrame *pFrame, int width, int height, int iFrame,char* dest_dir) ; void saveFrame(AVFrame *pFrame, int width, int height, int iFrame,char* dest_dir) { FILE *pFile; char szFilename[302]; int y; // Open file sprintf(szFilename, "%s%03d.ppm",dest_dir, iFrame); pFile=fopen(szFilename, "wb"); if(pFile==NULL){ fprintf(stderr,"Save Frame failed %s\n",szFilename); return; } // Write header fprintf(pFile, "P6\n%d %d\n255\n", width, height); // Write pixel data for(y=0; ydata[0]+y*pFrame->linesize[0], 1, width*3, pFile); // Close file fclose(pFile); } void saveYUV422Frame(AVFrame *pFrame, int width, int height, int iFrame,char* dest_dir,int numBytes) ; void saveYUV422Frame(AVFrame *pFrame, int width, int height, int iFrame,char* dest_dir,int numBytes) { FILE *YFile,*UFile,*VFile; char szFilename[302]; uint8_t* dataBuffer = pFrame->data[0]; int position = 0; // int pixelData[4]; int Y,U,V; int i; sprintf(szFilename, "%sY%03d.ppm",dest_dir, iFrame); YFile = fopen(szFilename,"wb"); if(YFile == NULL){ fprintf(stderr,"Save Frame failed %s\n",szFilename); return; } sprintf(szFilename, "%sU%03d.ppm",dest_dir, iFrame); UFile = fopen(szFilename,"wb"); if(UFile == NULL){ fprintf(stderr,"Save Frame failed %s\n",szFilename); return; } sprintf(szFilename, "%sV%03d.ppm",dest_dir, iFrame); VFile = fopen(szFilename,"wb"); if(VFile == NULL){ fprintf(stderr,"Save Frame failed %s\n",szFilename); return; } fprintf(YFile,"P5\n%d %d\n255\n",width,height); fprintf(UFile,"P5\n%d %d\n255\n",width/2,height); fprintf(VFile,"P5\n%d %d\n255\n",width/2,height); for(i = 0; i< numBytes;i++){ uint8_t b = dataBuffer[i]; if((position == 0) || (position == 2)){ Y = b; fwrite(&Y,sizeof(uint8_t),1,YFile); }else if( position == 1){ U = b; fwrite(&U,sizeof(uint8_t),1,UFile); }else{ V = b; fwrite(&V,sizeof(uint8_t),1,VFile); } position = (position + 1)%4; } fclose(YFile); fclose(UFile); fclose(VFile); } void initFFMPEG(void){ // Register all formats and codecs avcodec_register_all(); avdevice_register_all(); av_register_all(); } int initCaptureDevice(char* deviceName,AVFormatContext **pFormatCtx,AVCodecContext **pCodecCtx,AVCodec **pCodec,AVInputFormat **iformat,AVFormatParameters *formatParams,int* streamId); int initCaptureDevice(char* deviceName,AVFormatContext **pFormatCtx,AVCodecContext **pCodecCtx,AVCodec **pCodec,AVInputFormat **iformat,AVFormatParameters *formatParams,int* streamId){ int videoStream,i; //Init params with default options (*formatParams).standard = NULL; (*formatParams).width = 1280; (*formatParams).height = 1024; (*formatParams).time_base = (AVRational){1, 30}; (*iformat) = av_find_input_format("video4linux2"); //Detects if Video4Linux2 format is reconized fprintf(stderr,"Detecting Device Format..."); if (*iformat == NULL) { printf("No format found\n"); return -2; } fprintf(stderr,"OK\n"); // Open video input device fprintf(stderr,"Opening Device..."); if(av_open_input_file(pFormatCtx,deviceName, *iformat, 0, formatParams)!=0){ fprintf(stderr,"couldn't open device !\n"); return -1; // Couldn't open file } fprintf(stderr,"OK\n"); // Retrieve stream information fprintf(stderr,"Retrieving stream information..."); if(av_find_stream_info(*pFormatCtx)<0){ fprintf(stderr,"not found !\n"); return -1; // Couldn't find stream information } fprintf(stderr,"OK\n"); // Dump information about file onto standard error fprintf(stderr,"*********************Device Information**********************\n"); dump_format(*pFormatCtx, 0, deviceName, 0); fprintf(stderr,"*************************************************************\n"); // Find the first video stream avaiable fprintf(stderr,"Detecting video stream..."); videoStream=-1; for(i=0; i<(*pFormatCtx)->nb_streams; i++){ if((*pFormatCtx)->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) { videoStream=i; break; } } if(videoStream==-1){ fprintf(stderr,"not found !"); return -1; // Didn't find a video stream } *streamId = videoStream; fprintf(stderr,"OK\n"); // Get a pointer to the codec context for the video stream (*pCodecCtx) = (*pFormatCtx)->streams[videoStream]->codec; // Find the decoder for the video stream fprintf(stderr,"Locating device decoder..."); *pCodec=avcodec_find_decoder((*pCodecCtx)->codec_id); if(*pCodec==NULL) { fprintf(stderr, "unsupported codec !\n"); return -1; // Codec not found } // Open codec if(avcodec_open(*pCodecCtx, *pCodec)<0){ fprintf(stderr, "codec could not be opened !\n"); return -1; // Could not open codec } fprintf(stderr,"OK\n"); //OK, all damn things finished return 1; } AVFrame* allocFrame(AVCodecContext *pCodecCtx,int pixel_fmt,uint8_t **buffer,int* numBytes); AVFrame* allocFrame(AVCodecContext *pCodecCtx,int pixel_fmt,uint8_t **buffer,int* numBytes){ AVFrame* pFrameRGB; //int numBytes; //Alocate basic data structures, buffer is allocated in a different section (why ?) pFrameRGB=avcodec_alloc_frame(); if(pFrameRGB==NULL){ return NULL; } // Determine required buffer size and allocate buffer *numBytes=avpicture_get_size(pixel_fmt, pCodecCtx->width,pCodecCtx->height); *buffer=(uint8_t *)av_malloc(*numBytes*sizeof(uint8_t)); if(*buffer == NULL){ return NULL; } // Assign appropriate parts of buffer to image planes in pFrameRGB // Note that pFrameRGB is an AVFrame, but AVFrame is a superset of AVPicture avpicture_fill((AVPicture *)pFrameRGB, *buffer, pixel_fmt,pCodecCtx->width, pCodecCtx->height); return pFrameRGB; } void freeAVFrame(AVFrame* frame,uint8_t* buffer); void freeAVFrame(AVFrame* frame,uint8_t* buffer){ //Just free all needed structures if(buffer != NULL){ av_free(buffer); } av_free(frame); } void freeFrame( ffmpegFrame* f){ freeAVFrame(f->pFrame,f->data); free(f); } void convertFrameFormat(AVFrame* sourceFrame,int sourceWidth, int sourceHeight,int sourceFmt, AVFrame* destFrame,int destWidth, int destHeight,int destFmt); void convertFrameFormat(AVFrame* sourceFrame,int sourceWidth, int sourceHeight,int sourceFmt, AVFrame* destFrame,int destWidth, int destHeight,int destFmt){ //this function converts to a frame format to another, reescaling it if needed struct SwsContext* encoderSwsContext; encoderSwsContext = sws_getContext(sourceWidth, sourceHeight,sourceFmt , destWidth, destHeight,destFmt , SWS_BICUBIC, NULL, NULL, NULL); sws_scale(encoderSwsContext, sourceFrame->data, sourceFrame->linesize, 0, sourceHeight, destFrame->data, destFrame->linesize); } void captureFrame(AVFormatContext *pFormatCtx,AVCodecContext* pCodecCtx, AVFrame* bufferFrame,AVFrame* destFrame, int videoStream); void captureFrame(AVFormatContext *pFormatCtx,AVCodecContext* pCodecCtx, AVFrame* bufferFrame,AVFrame* destFrame, int videoStream){ AVPacket packet; int frameFinished = 0; while (!frameFinished){ if(av_read_frame(pFormatCtx, &packet) < 0){ fprintf(stderr, "WARNING... corrupted packet discarded\n"); //failed++; continue; } // Is this a packet from the video stream? if(packet.stream_index==videoStream) { // Decode video frame avcodec_decode_video(pCodecCtx, bufferFrame, &frameFinished,packet.data, packet.size); // Did we get a video frame? if(frameFinished) { //Copy frame from buffer to our vector if(destFrame != NULL) av_picture_copy((AVPicture *)destFrame,(AVPicture *)bufferFrame,pCodecCtx->pix_fmt,pCodecCtx->width, pCodecCtx->height); } } // Free the packet that was allocated by av_read_frame av_free_packet(&packet); } } int cameraHandlerGetLowLevelProperty(cameraHandler* ch, int property); int cameraHandlerGetLowLevelProperty(cameraHandler* ch, int property){ struct video_data *s = ch->pFormatCtx->priv_data; struct v4l2_format form; struct v4l2_control control; // int i, res; CLEAR(form); form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if(( property >= NUM_CAM_ATTRIBUTES) || (property < 0) ){ fprintf(stderr,"cameraHandlerGetLowLevelProperty - Invalid Property\n"); return 0; } if(ch->cam_attributes[property].enabled == 0){ fprintf(stderr,"Unable to get property %s: Property disabled by device driver",ch->cam_attributes[property].attribute_name); return 0; } if (-1 == xioctl(s->fd, VIDIOC_G_FMT, &form)) { /* display an error message, and return an error code */ fprintf(stderr,"Unable to query device: VIDIOC_G_FMT Error\n"); return -1; } control.id = ch->cam_attributes[property].attribute_id; if (-1 == xioctl (s->fd, VIDIOC_G_CTRL,&control)) { fprintf( stderr, "ERROR: cameraHandlerGetLowLevelProperty - "); fprintf(stderr,"%s", ch->cam_attributes[property].attribute_name); fprintf (stderr, " is not supported by your device\n"); return 0; } ch->cam_attributes[property].currentValue = control.value; return control.value; } int cameraHandlerSetLowLevelProperty(cameraHandler* ch, int property,int value); int cameraHandlerSetLowLevelProperty(cameraHandler* ch, int property,int value){ struct video_data *s = ch->pFormatCtx->priv_data; struct v4l2_control control; CLEAR(control); if(( property >= NUM_CAM_ATTRIBUTES) || (property < 0) ){ fprintf(stderr,"cameraHandlerSetLowLevelProperty - Invalid Property\n"); return 0; } if(ch->cam_attributes[property].enabled == 0){ fprintf(stderr,"Unable to set property %s: Property disabled by device driver",ch->cam_attributes[property].attribute_name); return 0; } control.id = ch->cam_attributes[property].attribute_id; if (-1 == xioctl (s->fd,VIDIOC_G_CTRL, &control)) { fprintf(stderr,"Unable to query device: VIDIOC_G_CTRL Error\n"); return -1; } CLEAR(control); control.id = ch->cam_attributes[property].attribute_id; control.value = value; if (-1 == xioctl (s->fd,VIDIOC_S_CTRL, &control) && errno != ERANGE) { fprintf(stderr,"Unable to set property %s:VIDIOC_S_CTRL",ch->cam_attributes[property].attribute_name); return -1; } ch->cam_attributes[property].currentValue = value; return 1; } cameraHandler* createCameraHandler(char* deviceName,int maxFrames){ cameraHandler* ch; ch = (cameraHandler*)malloc(sizeof(cameraHandler)); ch->deviceName = (char*)malloc(sizeof(char)*(strlen(deviceName) +1)); ch->maxFrames = maxFrames; ch->numFrames = 0; strcpy(ch->deviceName,deviceName); if(initCaptureDevice(ch->deviceName,&(ch->pFormatCtx),&(ch->pCodecCtx),&(ch->pCodec),&(ch->iformat),&(ch->formatParams),&(ch->videoStream)) < 0){ free(ch); return NULL; } //create frame buffer ch->bufferFrame.pFrame = avcodec_alloc_frame(); ch->bufferFrame.data = NULL; //create buffer to capture int i; ch->capturedFrames = NULL; if(maxFrames > 0){ ch->capturedFrames = (ffmpegFrame*)malloc(sizeof(ffmpegFrame)*maxFrames); } for(i = 0; i < ch->maxFrames; i++){ ch->capturedFrames[i].pFrame = allocFrame(ch->pCodecCtx,ch->pCodecCtx->pix_fmt,&(ch->capturedFrames[i].data),&(ch->capturedFrames[i].numBytes)); if(ch->capturedFrames[i].pFrame == NULL){ fprintf(stderr,"Failed allocating frame structure[%d]\n",i); } if(ch->capturedFrames[i].data == NULL){ fprintf(stderr,"Failed allocating frame buffer[%d]\n",i); } } /*this thing is to capture maximum and minimum parameters from camera using V4L2*/ struct v4l2_queryctrl queryctrl; __u32 ctrl_id; struct video_data* s = ch->pFormatCtx->priv_data; /*fprintf(stderr,"*********************Private Data****************************\n"); int l = sizeof(struct video_data)/sizeof(char); for(i = 0; i < l; i++){ uint8_t* bf = s; fprintf(stderr,"%03d ",bf[i]); } fprintf(stderr,"\n");*/ fprintf(stderr,"*********************Device Attributes***********************\n"); fprintf(stderr,"%p[%p] ==> %dx%d\n",(void*)s,(void*)s->fd,s->width,s->height); //for (ctrl_id = V4L2_CID_BASE;ctrl_id < V4L2_CID_LASTP1; ctrl_id++){ for (ctrl_id = 0;ctrl_id cam_attributes[ctrl_id] = cam_attributes_vector[ctrl_id]; queryctrl.id = ch->cam_attributes[ctrl_id].attribute_id; if (0 == xioctl (s->fd, VIDIOC_QUERYCTRL, &queryctrl)){ if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) continue; ch->cam_attributes[ctrl_id].minValue = queryctrl.minimum; ch->cam_attributes[ctrl_id].maxValue = queryctrl.maximum; ch->cam_attributes[ctrl_id].enabled = 1; //fprintf(stderr,"%s: %d-%d\n",ch->cam_attributes[ctrl_id].attribute_name,ch->cam_attributes[ctrl_id].minValue ,ch->cam_attributes[ctrl_id].maxValue = queryctrl.maximum); }else{ //fprintf(stderr,"Error...\n"); if (errno == EINVAL) continue; } } for (ctrl_id = 0;ctrl_id cam_attributes[ctrl_id].enabled == 1){ ch->cam_attributes[ctrl_id].currentValue = cameraHandlerGetLowLevelProperty(ch,ctrl_id); fprintf(stderr,"%s: Range[%d-%d] at %d\n",ch->cam_attributes[ctrl_id].attribute_name,ch->cam_attributes[ctrl_id].minValue ,ch->cam_attributes[ctrl_id].maxValue,ch->cam_attributes[ctrl_id].currentValue); } } fprintf(stderr,"*************************************************************\n"); return ch; } int cameraHandlerCapture(cameraHandler* ch,ffmpegFrame* frame){ AVFrame* destFrame = NULL; if(frame != NULL){ destFrame = frame->pFrame; }else if( ch->numFrames < ch->maxFrames){ destFrame = ch->capturedFrames[ch->numFrames].pFrame; ch->numFrames++; }else{ fprintf(stderr,"Camera Handler buffer full... frame will be discarded\n"); } if(destFrame != NULL){ captureFrame(ch->pFormatCtx,ch->pCodecCtx,ch->bufferFrame.pFrame,destFrame,ch->videoStream); return 1; }else{ captureFrame(ch->pFormatCtx,ch->pCodecCtx,ch->bufferFrame.pFrame,NULL,ch->videoStream); return 0; } } int cameraHandlerGetProperty(cameraHandler* ch,int property ){ switch(property){ case CH_FRAME_HEIGHT: return ch->pCodecCtx->height; break; case CH_FRAME_WIDTH: return ch->pCodecCtx->width; break; case CH_NUM_FRAMES: return ch->numFrames; break; case CH_MAX_FRAMES: return ch->maxFrames; break; case CH_FRAME_FMT: return ch->pCodecCtx->pix_fmt; break; default: return cameraHandlerGetLowLevelProperty(ch,property); }; } camera_attributes* cameraHandlerGetPropertyInfo(cameraHandler* ch, int property){ camera_attributes* response; response = (camera_attributes*)malloc(sizeof(camera_attributes)); if((property < 0) || (property >= NUM_CAM_ATTRIBUTES)){ fprintf(stderr,"cameraHandlerGetPropertyMaxValue - Invalid Property\n"); return NULL; } response->attribute_id = ch->cam_attributes[property].attribute_id; response->maxValue = ch->cam_attributes[property].maxValue; response->minValue = ch->cam_attributes[property].minValue; response->currentValue = ch->cam_attributes[property].currentValue; response->enabled = ch->cam_attributes[property].enabled; response->attribute_name = (char*)malloc(sizeof(char)*(strlen(ch->cam_attributes[property].attribute_name) +1 )); strcpy(response->attribute_name,ch->cam_attributes[property].attribute_name); return response; } int cameraHandlerSetProperty(cameraHandler* ch, int property, int value){ return cameraHandlerSetLowLevelProperty(ch,property,value); } ffmpegFrame* createFrame(cameraHandler* ch){ ffmpegFrame* frame; frame = (ffmpegFrame*)malloc(sizeof(ffmpegFrame)); frame->pFrame = allocFrame(ch->pCodecCtx,ch->pCodecCtx->pix_fmt,&(frame->data),&(frame->numBytes)); return frame; } void cameraHandlerRelease(cameraHandler* ch){ //free temp. buffer //fprintf(stderr,"Releasing Temp Buffer\n"); av_free(ch->bufferFrame.pFrame); int i; //free captured data //fprintf(stderr,"Releasing Capture Buffer\n"); for(i = 0;i < ch->maxFrames; i++){ // printf("%d..\n",i); freeAVFrame(ch->capturedFrames[i].pFrame,ch->capturedFrames[i].data); } //fprintf(stderr,"Releasing Capture Buffer structure\n"); //free captured frames array free(ch->capturedFrames); // Close the codec //fprintf(stderr,"Releasing Codecs\n"); avcodec_close(ch->pCodecCtx); //Close the video file av_close_input_file(ch->pFormatCtx); } void cameraHandlerSaveFrame(cameraHandler* ch, ffmpegFrame* frame,char* destDir,int frameNumber){ uint8_t* buffer; int numBytes; AVFrame* pFrameRGB = allocFrame(ch->pCodecCtx,PIX_FMT_RGB24,&buffer,&numBytes); convertFrameFormat(frame->pFrame,ch->pCodecCtx->width, ch->pCodecCtx->height,ch->pCodecCtx->pix_fmt, pFrameRGB,ch->pCodecCtx->width, ch->pCodecCtx->height,PIX_FMT_RGB24); saveFrame(pFrameRGB,ch->pCodecCtx->width, ch->pCodecCtx->height,frameNumber,destDir); freeAVFrame(pFrameRGB,buffer); } void cameraHandlerSaveYUVFrame(cameraHandler* ch, ffmpegFrame* frame,char* destDir,int frameNumber){ saveYUV422Frame(frame->pFrame, ch->pCodecCtx->width, ch->pCodecCtx->height, frameNumber ,destDir,frame->numBytes); } void cameraHandlerSaveAllFrames(cameraHandler* ch, char* destDir){ int i; for(i = 0; i < ch->numFrames; i++){ cameraHandlerSaveFrame(ch,&(ch->capturedFrames[i]),destDir,i); } } ffmpegFrame* cameraHandlerGetCapturedFrame(cameraHandler* ch,int index){ if(( index < ch->numFrames) && (index >= 0) ){ return &ch->capturedFrames[index]; } return NULL; } void* getFramePointer(ffmpegFrame* f){ return f->pFrame; }