实战小项目之基于嵌入式的图像采集压缩保存
项目简介
这是之前图像采集显示程序的升级版,首先基础部分的图像v4l2采集、framebuffer显示、IPU转码都进行了c++封装,之后加入了以下新功能:
- SDL1.2显示
- FFmpeg编码保存版本一(顺序执行)
- FFmpeg编码保存版本二(线程方式)
- c++ pthread库简单封装
这个小工程是一个附属产品,boss的项目中用到了图像编码保存,然后学了一段时间的多媒体技术(主要就是FFmpeg),后来就衍生出了这个版本的程序
不多说,上代码
首先是SDL显示部分
/* * SDLDisp.cpp * * Created on: Oct 11, 2016 * Author: tla001 */ #include "SDLDisp.h" /* * display different data * 1.framesize 420p:screen_w*screen_h*3/2 422:screen_w*screen_h*2 * 2.SDL_CreateYUVOverlay 420p:SDL_IYUV_OVERLAY 422:SDL_YUY2_OVERLAY * 3.dispfuction 420p:DisplayYUV420P 422:DisplayYUV422 */ SDLDisp::SDLDisp(int screen_w,int screen_h) { // TODO Auto-generated constructor stub this->screen_w=screen_w; this->screen_h=screen_h; this->framesize=screen_w*screen_h*3/2; this->buffer=NULL; this->sdl_running=1; this->thread_exit=0; } SDLDisp::~SDLDisp() { // TODO Auto-generated destructor stub this->sdl_running=0; closeSDL(); } int SDLDisp::initSDL(char *winName) { buffer=(uint8_t*)malloc(sizeof(uint8_t)*framesize); SDL_Init(SDL_INIT_EVERYTHING); screen = SDL_SetVideoMode(screen_w, screen_h, 0, SDL_SWSURFACE|SDL_ANYFORMAT); if(!screen) { printf("SDL: could not set video mode - exiting:%s\n",SDL_GetError()); return -1; } /* Set the window name */ SDL_WM_SetCaption(winName, NULL); //yuv420P run with DisplayYUV420P // overlay = SDL_CreateYUVOverlay(screen_w, screen_h,SDL_IYUV_OVERLAY, screen); //yuv422 run with DisplayYUV422 overlay = SDL_CreateYUVOverlay(screen_w, screen_h,SDL_IYUV_OVERLAY, screen); rect.x=0; rect.y = 0; rect.w = screen_w; rect.h = screen_h; //refresh_thread = SDL_CreateThread(sdlcontol,NULL); //pthread_create(&controlTid,NULL,sdlcontol, static_cast<void*>(this)); //初始化读写所 // int ret=pthread_rwlock_init(&rwlock, NULL); // if(ret<0){ // printf("init wrlock failed\n"); // return -1; // } //pthread_create(&dispTid,NULL,&sdlDisp,NULL); return 0; } int SDLDisp::closeSDL() { if(buffer!=NULL) free(buffer); thread_exit=1; SDL_FreeYUVOverlay(overlay); SDL_FreeSurface(screen); SDL_Quit(); printf("sdl end\n"); return 0; } int SDLDisp::sdlDisp() { printf("sdl disp start\n"); while(sdl_running){ SDL_WaitEvent(&event); if(event.type==REFRESH_EVENT){ SDL_LockSurface(screen); pthread_rwlock_rdlock(&rwlock); DisplayYUV420P(buffer, screen_w, screen_h, overlay); pthread_rwlock_unlock(&rwlock); SDL_UnlockSurface(screen); SDL_DisplayYUVOverlay(overlay, &rect); //Update Screen SDL_Flip(screen); }else if(event.type==SDL_QUIT){ thread_exit=1; sdl_running=0; }else if(event.type==BREAK_EVENT){ break; } } thread_exit=1; printf("sdl disp end\n"); return 0; } int SDLDisp::refresh_video(void *opaque){ thread_exit=0; while (thread_exit==0) { SDL_Event event; event.type = REFRESH_EVENT; SDL_PushEvent(&event); SDL_Delay(40); } thread_exit=0; //Break SDL_Event event; event.type = BREAK_EVENT; SDL_PushEvent(&event); return 0; } int SDLDisp::doSDLDisp(uint8_t *buf) { pthread_rwlock_wrlock(&rwlock); memcpy(buffer,buf,framesize); pthread_rwlock_unlock(&rwlock); return 0; } void SDLDisp::normalSDLDisp(uint8_t *buf) { memcpy(buffer,buf,framesize); SDL_LockSurface(screen); DisplayYUV420P(buffer, screen_w, screen_h, overlay);//420p //DisplayYUV422(buffer, screen_w, screen_h, overlay);//422 SDL_UnlockSurface(screen); SDL_DisplayYUVOverlay(overlay, &rect); //Update Screen SDL_Flip(screen); } void SDLDisp::DisplayYUV420P(uint8_t *buf, uint32_t w, uint32_t h, SDL_Overlay *overlay) { /* Fill in video data */ //uint32_t yuv_size = (w*h) * 3/2; uint8_t * y_video_data = (uint8_t*)buf; uint8_t * u_video_data = (uint8_t*)(buf + w * h); uint8_t * v_video_data = (uint8_t*)(u_video_data + (w * h / 4)); /* Fill in pixel data - the pitches array contains the length of a line in each plane */ SDL_LockYUVOverlay(overlay); memcpy(overlay->pixels[0], y_video_data, w * h); memcpy(overlay->pixels[1], u_video_data, w * h / 4); memcpy(overlay->pixels[2], v_video_data, w * h / 4); SDL_UnlockYUVOverlay(overlay); } void SDLDisp::DisplayYUV420(uint8_t *buf, uint32_t w, uint32_t h, SDL_Overlay *overlay) { /* Fill in video data */ //uint32_t yuv_size = (w*h) * 3/2; uint8_t * y_video_data = (uint8_t*)buf; uint8_t * v_video_data = (uint8_t*)(buf + w * h); uint8_t * u_video_data = (uint8_t*)(v_video_data + (w * h / 4)); /* Fill in pixel data - the pitches array contains the length of a line in each plane */ SDL_LockYUVOverlay(overlay); memcpy(overlay->pixels[0], y_video_data, w * h); memcpy(overlay->pixels[1], u_video_data, w * h / 4); memcpy(overlay->pixels[2], v_video_data, w * h / 4); SDL_UnlockYUVOverlay(overlay); } void SDLDisp::DisplayYUV422(uint8_t *buf, uint32_t w, uint32_t h, SDL_Overlay *overlay) { /* Fill in pixel data - the pitches array contains the length of a line in each plane */ SDL_LockYUVOverlay(overlay); memcpy(overlay->pixels[0], buf, w * h*2); SDL_UnlockYUVOverlay(overlay); }
然后是encodesaver1
/* * EncodeSaver.cpp * * Created on: Oct 11, 2016 * Author: tla001 */ #include "EncodeSaver.h" EncodeSaver::EncodeSaver(int in_w,int in_h,int out_w,int out_h,char* out_file) { // TODO Auto-generated constructor stub this->in_w=in_w; this->in_h=in_h; this->out_w=out_w; this->out_h=out_h; this->framesize=0; this->out_file=out_file; this->basicsize=in_w*in_h; this->frameNum=0; } EncodeSaver::~EncodeSaver() { // TODO Auto-generated destructor stub } int EncodeSaver::initDevice() { av_register_all(); //方法1.组合使用几个函数 pFormatCtx = avformat_alloc_context(); //猜格式 fmt = av_guess_format(NULL, out_file, NULL); pFormatCtx->oformat = fmt; if (avio_open(&pFormatCtx->pb,out_file, AVIO_FLAG_READ_WRITE) < 0) { printf("输出文件打开失败"); return -1; } video_st = av_new_stream(pFormatCtx, 0); if (video_st==NULL) { return -1; } pCodecCtx = video_st->codec; pCodecCtx->codec_id = fmt->video_codec; pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO; pCodecCtx->pix_fmt = PIX_FMT_YUV420P; pCodecCtx->width = in_w; pCodecCtx->height = in_h; pCodecCtx->time_base.num = 1; pCodecCtx->time_base.den = 18; pCodecCtx->bit_rate = 400000; pCodecCtx->gop_size=250; //设置 video_st->time_base.num=1; video_st->time_base.den=18; //H264 pCodecCtx->me_range = 16; pCodecCtx->max_qdiff = 4; pCodecCtx->qmin = 10; pCodecCtx->qmax = 51; pCodecCtx->qcompress = 0.6; if (pCodecCtx->codec_id == CODEC_ID_MPEG2VIDEO) { pCodecCtx->max_b_frames = 2; } if (pCodecCtx->codec_id == CODEC_ID_MPEG1VIDEO) { pCodecCtx->mb_decision = 2; } if (!strcmp(pFormatCtx->oformat->name, "mp4") || !strcmp(pFormatCtx->oformat->name, "mov") || !strcmp(pFormatCtx->oformat->name, "3gp")) { pCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER; } //输出格式信息 av_dump_format(pFormatCtx, 0, out_file, 1); pCodec = avcodec_find_encoder(pCodecCtx->codec_id); if (!pCodec) { printf("没有找到合适的编码器!\n"); return -1; } if (avcodec_open2(pCodecCtx, pCodec,NULL) < 0) { printf("编码器打开失败!\n"); return -1; } //输出420 picture = avcodec_alloc_frame(); framesize = avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); picture_buf = (uint8_t *)av_malloc(framesize); avpicture_fill((AVPicture *)picture, picture_buf, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); //输入422 picture422 = avcodec_alloc_frame(); framesize422 = avpicture_get_size(PIX_FMT_YUYV422, pCodecCtx->width, pCodecCtx->height); picture422_buf = (uint8_t *)av_malloc(framesize422); avpicture_fill((AVPicture *)picture422, picture422_buf, PIX_FMT_YUYV422, pCodecCtx->width, pCodecCtx->height); img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUYV422, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); //写文件头 avformat_write_header(pFormatCtx,NULL); av_new_packet(&pkt,basicsize*3); } int EncodeSaver::closeDevice() { int ret = flush_encoder(pFormatCtx,0); if (ret < 0) { printf("Flushing encoder failed\n"); return -1; } //写文件尾 av_write_trailer(pFormatCtx); //清理 if (video_st) { avcodec_close(video_st->codec); av_free(picture); av_free(picture_buf); av_free(picture422); av_free(picture422_buf); } avio_close(pFormatCtx->pb); avformat_free_context(pFormatCtx); return 0; } int EncodeSaver::doEncode(unsigned char *buf) { int ret; //420P memcpy(picture_buf,buf,basicsize*3/2); picture->data[0] = picture_buf; // 亮度Y picture->data[1] = picture_buf+ basicsize; // U picture->data[2] = picture_buf+ basicsize*5/4; // V //PTS if (pFormatCtx->oformat->flags & AVFMT_RAWPICTURE) { AVPacket pkt; av_init_packet(&pkt); pkt.flags |= AV_PKT_FLAG_KEY; pkt.stream_index = video_st->index; pkt.data = (uint8_t*)picture; pkt.size = sizeof(AVPicture); ret = av_write_frame(pFormatCtx, &pkt); } else{ int out_size = avcodec_encode_video(pCodecCtx, picture_buf, framesize, picture); if (out_size > 0) { AVPacket pkt; av_init_packet(&pkt); pkt.pts = av_rescale_q(pCodecCtx->coded_frame->pts, pCodecCtx->time_base, video_st->time_base); if (pCodecCtx->coded_frame->key_frame) { pkt.flags |= AV_PKT_FLAG_KEY; printf("here 1\n"); } pkt.stream_index = video_st->index; pkt.data = picture_buf; pkt.size = out_size; ret = av_write_frame(pFormatCtx, &pkt); } } return 0; } int EncodeSaver::doEncode2(unsigned char *buf) { int ret; //422 memcpy(picture422_buf,buf,basicsize*2); picture422->data[0] = picture422_buf; sws_scale(img_convert_ctx, picture422->data, picture422->linesize, 0, pCodecCtx->height, picture->data, picture->linesize); //PTS if (pFormatCtx->oformat->flags & AVFMT_RAWPICTURE) { AVPacket pkt; av_init_packet(&pkt); pkt.flags |= AV_PKT_FLAG_KEY; pkt.stream_index = video_st->index; pkt.data = (uint8_t*)picture; pkt.size = sizeof(AVPicture); ret = av_write_frame(pFormatCtx, &pkt); } else{ int out_size = avcodec_encode_video(pCodecCtx, picture_buf, framesize, picture); if (out_size > 0) { AVPacket pkt; av_init_packet(&pkt); pkt.pts = av_rescale_q(pCodecCtx->coded_frame->pts, pCodecCtx->time_base, video_st->time_base); if (pCodecCtx->coded_frame->key_frame) { pkt.flags |= AV_PKT_FLAG_KEY; } pkt.stream_index = video_st->index; pkt.data = picture_buf; pkt.size = out_size; ret = av_write_frame(pFormatCtx, &pkt); } } return ret; } int EncodeSaver::flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index) { int ret; int got_frame; AVPacket enc_pkt; if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities & CODEC_CAP_DELAY)) return 0; while (1) { printf("Flushing stream #%u encoder\n", stream_index); //ret = encode_write_frame(NULL, stream_index, &got_frame); enc_pkt.data = NULL; enc_pkt.size = 0; av_init_packet(&enc_pkt); ret = avcodec_encode_video2 (fmt_ctx->streams[stream_index]->codec, &enc_pkt, NULL, &got_frame); av_frame_free(NULL); if (ret < 0) break; if (!got_frame) {ret=0;break;} printf("successed frame!\n"); /* mux encoded frame */ ret = av_write_frame(fmt_ctx, &enc_pkt); if (ret < 0) break; } return ret; }
接着是encodesaver2
/* * EncodeSaver2.cpp * * Created on: Oct 12, 2016 * Author: tla001 */ #include "EncodeSaver2.h" EncodeSaver2::EncodeSaver2(int in_w,int in_h,int out_w,int out_h,char* out_file) { // TODO Auto-generated constructor stub this->in_w=in_w; this->in_h=in_h; this->out_w=out_w; this->out_h=out_h; this->framesize=0; this->out_file=out_file; this->basicsize=in_w*in_h; this->frameNum=0; this->ready=0; } EncodeSaver2::~EncodeSaver2() { // TODO Auto-generated destructor stub closeDevice(); } int EncodeSaver2::initDevice() { av_register_all(); //方法1.组合使用几个函数 pFormatCtx = avformat_alloc_context(); //猜格式 fmt = av_guess_format(NULL, out_file, NULL); pFormatCtx->oformat = fmt; if (avio_open(&pFormatCtx->pb,out_file, AVIO_FLAG_READ_WRITE) < 0) { printf("输出文件打开失败"); return -1; } video_st = av_new_stream(pFormatCtx, 0); if (video_st==NULL) { return -1; } pCodecCtx = video_st->codec; pCodecCtx->codec_id = fmt->video_codec; pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO; pCodecCtx->pix_fmt = PIX_FMT_YUV420P; pCodecCtx->width = in_w; pCodecCtx->height = in_h; pCodecCtx->time_base.num = 1; pCodecCtx->time_base.den = 25; pCodecCtx->bit_rate = 400000; pCodecCtx->gop_size=250; //设置 video_st->time_base.num=1; video_st->time_base.den=25; //H264 pCodecCtx->me_range = 16; pCodecCtx->max_qdiff = 4; pCodecCtx->qmin = 10; pCodecCtx->qmax = 51; pCodecCtx->qcompress = 0.6; if (pCodecCtx->codec_id == CODEC_ID_MPEG2VIDEO) { pCodecCtx->max_b_frames = 2; } if (pCodecCtx->codec_id == CODEC_ID_MPEG1VIDEO) { pCodecCtx->mb_decision = 2; } if (!strcmp(pFormatCtx->oformat->name, "mp4") || !strcmp(pFormatCtx->oformat->name, "mov") || !strcmp(pFormatCtx->oformat->name, "3gp")) { pCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER; } //输出格式信息 av_dump_format(pFormatCtx, 0, out_file, 1); pCodec = avcodec_find_encoder(pCodecCtx->codec_id); if (!pCodec) { printf("没有找到合适的编码器!\n"); return -1; } if (avcodec_open2(pCodecCtx, pCodec,NULL) < 0) { printf("编码器打开失败!\n"); return -1; } //输出420 picture = avcodec_alloc_frame(); framesize = avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); picture_buf = (uint8_t *)av_malloc(framesize); avpicture_fill((AVPicture *)picture, picture_buf, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); //输入422 picture422 = avcodec_alloc_frame(); framesize422 = avpicture_get_size(PIX_FMT_YUYV422, pCodecCtx->width, pCodecCtx->height); picture422_buf = (uint8_t *)av_malloc(framesize422); avpicture_fill((AVPicture *)picture422, picture422_buf, PIX_FMT_YUYV422, pCodecCtx->width, pCodecCtx->height); img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUYV422, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); //写文件头 avformat_write_header(pFormatCtx,NULL); av_new_packet(&pkt,basicsize*3); this->start(); cout<<"thread start"<<endl; } int EncodeSaver2::closeDevice() { this->stop(); int ret = flush_encoder(pFormatCtx,0); if (ret < 0) { printf("Flushing encoder failed\n"); return -1; } //写文件尾 av_write_trailer(pFormatCtx); //清理 if (video_st) { avcodec_close(video_st->codec); av_free(picture); av_free(picture_buf); av_free(picture422); av_free(picture422_buf); } avio_close(pFormatCtx->pb); avformat_free_context(pFormatCtx); return 0; } int EncodeSaver2::doEncode(unsigned char *buf) { int ret=0; //422 memcpy(picture422_buft.a,buf,basicsize*2); //picture422_bufs.push(picture422_buft); this->ready=1; return ret; } int EncodeSaver2::isReady(){ return ready; } void EncodeSaver2::run(){ int ret; struct timeval temp; temp.tv_sec = 0; temp.tv_usec = 40000000;//40ms printf("thread is up!\n"); while(1){ if(isStop()) pthread_exit(0); if(isStart()&&isReady()){ // printf("here 1\n"); // sleep(1); //select(0, NULL, NULL, NULL, &temp); //usleep(40000000); printf("**********time up********ttkf*** \n"); // if(picture422_bufs.empty()){ // cout<<" queue empty"<<endl; // continue; // } //picture422_buf=picture422_bufs.front().a; //picture422_bufs.pop(); memcpy(picture422_buf,picture422_buft.a,basicsize*2); picture422->data[0] = picture422_buf; sws_scale(img_convert_ctx, picture422->data, picture422->linesize, 0, pCodecCtx->height, picture->data, picture->linesize); //PTS if (pFormatCtx->oformat->flags & AVFMT_RAWPICTURE) { AVPacket pkt; av_init_packet(&pkt); pkt.flags |= AV_PKT_FLAG_KEY; pkt.stream_index = video_st->index; pkt.data = (uint8_t*)picture; pkt.size = sizeof(AVPicture); ret = av_write_frame(pFormatCtx, &pkt); } else{ int out_size = avcodec_encode_video(pCodecCtx, picture_buf, framesize, picture); if (out_size > 0) { AVPacket pkt; av_init_packet(&pkt); pkt.pts = av_rescale_q(pCodecCtx->coded_frame->pts, pCodecCtx->time_base, video_st->time_base); if (pCodecCtx->coded_frame->key_frame) { pkt.flags |= AV_PKT_FLAG_KEY; } pkt.stream_index = video_st->index; pkt.data = picture_buf; pkt.size = out_size; ret = av_write_frame(pFormatCtx, &pkt); } } }else{ printf("ready for data\n"); usleep(50000);//线程起来之后,由于参数没准备好,可能会空转 } } } int EncodeSaver2::flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index) { int ret; int got_frame; AVPacket enc_pkt; if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities & CODEC_CAP_DELAY)) return 0; while (1) { printf("Flushing stream #%u encoder\n", stream_index); //ret = encode_write_frame(NULL, stream_index, &got_frame); enc_pkt.data = NULL; enc_pkt.size = 0; av_init_packet(&enc_pkt); ret = avcodec_encode_video2 (fmt_ctx->streams[stream_index]->codec, &enc_pkt, NULL, &got_frame); av_frame_free(NULL); if (ret < 0) break; if (!got_frame) {ret=0;break;} printf("successed frame!\n"); /* mux encoded frame */ ret = av_write_frame(fmt_ctx, &enc_pkt); if (ret < 0) break; } return ret; }
完整工程
https://github.com/tla001/CapTransV2
相关链接
ffmpeg移植
http://www.cnblogs.com/tla001/p/5906220.html
音视频处理(FFmpeg)基础
http://blog.csdn.net/leixiaohua1020/article/details/15811977
学习的资料,基本都是从雷神的博客中来的
相关命令
ffmpeg -f video4linux2 -i /dev/video0 -vcodec libx264 -s 320*240 -r 10 video0.mkv ffmpeg -f video4linux2 -s 640*480 -r 25 -i /dev/video0 test.avi
转载请注明出处:http://www.cnblogs.com/tla001/
一起学习,一起进步