另一篇博客《YUV格式介绍》有yuyv和yuv420p的理论介绍,本篇博客为具体转换的实际代码以及验证用的代码,代码均来源于其他博客,经实际验证有效,在此记录防止遗忘。
yuyv转yuv420p:
unsigned char *yuyv_buf = (unsigned char *)malloc(2 * width * height * sizeof(unsigned char)); unsigned char *yuv420_buf = (unsigned char *)malloc(3 * width * height / 2 * sizeof(unsigned char)); int yuyv_to_yuv420p(const unsigned char *in, unsigned char *out, unsigned int width, unsigned int height) { unsigned char *y = out; unsigned char *u = out + width*height; unsigned char *v = out + width*height + width*height/4; unsigned int i,j; unsigned int base_h; unsigned int is_y = 1, is_u = 1; unsigned int y_index = 0, u_index = 0, v_index = 0; unsigned long yuv422_length = 2 * width * height; //序列为YU YV YU YV,一个yuv422帧的长度 width * height * 2 个字节 //丢弃偶数行 u v for(i=0; i<yuv422_length; i+=2){ *(y+y_index) = *(in+i); y_index++; } for(i=0; i<height; i+=2){ base_h = i*width*2; for(j=base_h+1; j<base_h+width*2; j+=2) { if(is_u){ *(u+u_index) = *(in+j); u_index++; is_u = 0; }else{ *(v+v_index) = *(in+j); v_index++; is_u = 1; } } } return 1; }
验证的话只需要将数据转换成rgb看是否正长即可知道转换是否有问题:
yuyv转rgb:
void yuyv_to_rgb(unsigned char* yuv,unsigned char* rgb) { unsigned int i; unsigned char* y0 = yuv + 0; unsigned char* u0 = yuv + 1; unsigned char* y1 = yuv + 2; unsigned char* v0 = yuv + 3; unsigned char* r0 = rgb + 0; unsigned char* g0 = rgb + 1; unsigned char* b0 = rgb + 2; unsigned char* r1 = rgb + 3; unsigned char* g1 = rgb + 4; unsigned char* b1 = rgb + 5; float rt0 = 0, gt0 = 0, bt0 = 0, rt1 = 0, gt1 = 0, bt1 = 0; for(i = 0; i <= (WIDTH * HEIGHT) / 2 ;i++) { bt0 = 1.164 * (*y0 - 16) + 2.018 * (*u0 - 128); gt0 = 1.164 * (*y0 - 16) - 0.813 * (*v0 - 128) - 0.394 * (*u0 - 128); rt0 = 1.164 * (*y0 - 16) + 1.596 * (*v0 - 128); bt1 = 1.164 * (*y1 - 16) + 2.018 * (*u0 - 128); gt1 = 1.164 * (*y1 - 16) - 0.813 * (*v0 - 128) - 0.394 * (*u0 - 128); rt1 = 1.164 * (*y1 - 16) + 1.596 * (*v0 - 128); if(rt0 > 250) rt0 = 255; if(rt0< 0) rt0 = 0; if(gt0 > 250) gt0 = 255; if(gt0 < 0) gt0 = 0; if(bt0 > 250) bt0 = 255; if(bt0 < 0) bt0 = 0; if(rt1 > 250) rt1 = 255; if(rt1 < 0) rt1 = 0; if(gt1 > 250) gt1 = 255; if(gt1 < 0) gt1 = 0; if(bt1 > 250) bt1 = 255; if(bt1 < 0) bt1 = 0; *r0 = (unsigned char)rt0; *g0 = (unsigned char)gt0; *b0 = (unsigned char)bt0; *r1 = (unsigned char)rt1; *g1 = (unsigned char)gt1; *b1 = (unsigned char)bt1; yuv = yuv + 4; rgb = rgb + 6; if(yuv == NULL) break; y0 = yuv; u0 = yuv + 1; y1 = yuv + 2; v0 = yuv + 3; r0 = rgb + 0; g0 = rgb + 1; b0 = rgb + 2; r1 = rgb + 3; g1 = rgb + 4; b1 = rgb + 5; } }
yuv420p转rgb:
bool YV12ToBGR24_FFmpeg(unsigned char* pYUV,unsigned char* pBGR24,int width,int height) { if (width < 1 || height < 1 || pYUV == NULL || pBGR24 == NULL) return false; //int srcNumBytes,dstNumBytes; //uint8_t *pSrc,*pDst; AVPicture pFrameYUV,pFrameBGR; //pFrameYUV = avpicture_alloc(); //srcNumBytes = avpicture_get_size(PIX_FMT_YUV420P,width,height); //pSrc = (uint8_t *)malloc(sizeof(uint8_t) * srcNumBytes); avpicture_fill(&pFrameYUV,pYUV,PIX_FMT_YUV420P,width,height); //U,V互换 uint8_t * ptmp=pFrameYUV.data[1]; pFrameYUV.data[1]=pFrameYUV.data[2]; pFrameYUV.data [2]=ptmp; //pFrameBGR = avcodec_alloc_frame(); //dstNumBytes = avpicture_get_size(PIX_FMT_BGR24,width,height); //pDst = (uint8_t *)malloc(sizeof(uint8_t) * dstNumBytes); avpicture_fill(&pFrameBGR,pBGR24,PIX_FMT_BGR24,width,height); struct SwsContext* imgCtx = NULL; imgCtx = sws_getContext(width,height,PIX_FMT_YUV420P,width,height,PIX_FMT_BGR24,SWS_BILINEAR,0,0,0); if (imgCtx != NULL){ sws_scale(imgCtx,pFrameYUV.data,pFrameYUV.linesize,0,height,pFrameBGR.data,pFrameBGR.linesize); if(imgCtx){ sws_freeContext(imgCtx); imgCtx = NULL; } return true; } else{ sws_freeContext(imgCtx); imgCtx = NULL; return false; } }
拿到RGB数据后可以用Qt的QImage类保存成本地图片文件以查验转换是否成功。