yuyv422的数据来自ffdshow raw filter输出的yuv2, 其实就是yuv422p的packed模式, 要转成planar模式。
对原始颜色空间的文件不确定是不是你想的那种颜色空间的话请先用ffplay播放一下看看正不正确,ffplay支持的颜色空间可以通过:
//视频 ffplay -pix_fmts //音频 ffplay -sample_fmts来查看支持的格式, 播放可通过:
ffplay -framerate 25 -pixel_format yuyv422 -video_size 640x480 -i yuy2.yuv
如果播放出来的效果跟你想的一样那就对了。
由于yuv颜色空间各种名称各种规格多的莫名其妙,根据yuv的比例主要422、420、444三种, 排列格式有packed和planar,packed就是只要一个指针指向数据就行了, 而planar需要三个指针分别指向y、u、v的地址。 yuv420 planar的比如I420,YV12, 其实两者关系就是u和v的位置互换而已。 算了越说越糊涂。
转换用大家都喜欢的libswscale, 说实在的这东西光看头文件容易犯晕。 直接上代码:
void ConvertYUYV2ToYUV422P() { int w = 640; int h = 480; uint8_t *in_buf = new uint8_t[614400 + 4]; uint8_t *out_buf = new uint8_t[614400 + 4]; //auto m_av_input_frame = avcodec_alloc_frame(); //auto m_av_output_frame = avcodec_alloc_frame(); //auto aia_in = av_image_alloc(m_av_input_frame->data, m_av_input_frame->linesize, // w, h, PIX_FMT_YUYV422, 1); //auto aia_out = av_image_alloc(m_av_output_frame->data, m_av_output_frame->linesize, // w, h, PIX_FMT_YUV422P, 1); AVPicture m_av_input_pic; AVPicture m_av_output_pic; auto one_yuy2_size = avpicture_get_size(PIX_FMT_YUYV422, w, h); auto one_yuv422p_size = avpicture_get_size(PIX_FMT_YUV422P, w, h); //avpicture_alloc(&m_av_input_pic, PIX_FMT_YUYV422, w, h); avpicture_alloc(&m_av_output_pic, PIX_FMT_YUV422P, w, h); std::fstream input_file, output_file; input_file.open("e:\\640_480_yuy2.yuv", std::ios_base::in | std::ios_base::binary); output_file.open("e:\\640_480_yuv422p.yuv", std::ios_base::out | std::ios_base::binary); auto s_input = sws_isSupportedInput(PIX_FMT_YUYV422); auto s_output = sws_isSupportedOutput(PIX_FMT_YUYV422); auto sws_ct = sws_getCachedContext(NULL, w, h, PIX_FMT_YUYV422, w, h, PIX_FMT_YUV422P, SWS_BILINEAR, NULL, NULL, NULL); while (!input_file.eof()) { input_file.read((char *)in_buf, one_yuy2_size); auto rel_size = input_file.gcount(); if (rel_size != one_yuy2_size) break; auto fill_size = avpicture_fill(&m_av_input_pic, in_buf, PIX_FMT_YUYV422, w, h); auto sws_rel = sws_scale(sws_ct, m_av_input_pic.data, m_av_input_pic.linesize, 0, h, m_av_output_pic.data, m_av_output_pic.linesize); auto layout_rel = avpicture_layout(&m_av_output_pic, PIX_FMT_YUV422P, w, h, out_buf, 614404); AtlTrace("Fill PIX_FMT_YUY2, Size:[%d], sws_scale return:[%d], layout return:[%d]\n", fill_size, sws_rel, layout_rel); output_file.write((char *)out_buf, layout_rel); } input_file.close(); output_file.close(); //av_free(m_av_input_frame->data[0]); //av_free(m_av_input_frame); //av_free(m_av_output_frame->data[0]); //av_free(m_av_output_frame); //avpicture_free(&m_av_input_pic); avpicture_free(&m_av_output_pic); sws_freeContext(sws_ct); delete [] in_buf; delete [] out_buf; }
注释掉的那几行可以完全删除, 那只是我走的弯路。 至于sws_getCachedContext为什么要 SWS_BILINEAR, 我翻了翻libswscale的代码, 写的太逆天了我就不深究了。
代码来自[这里]
void FlipFrame(AVFrame* pFrame) { for (int i = 0; i < 4; i++) { pFrame->data[i] += pFrame->linesize[i] * (pFrame->height-1); pFrame->linesize[i] = -pFrame->linesize[i]; } } void FlipAVPicture(AVPicture* pFrame, int height) { for (int i = 0; i < 4; i++) { pFrame->data[i] += pFrame->linesize[i] * (height-1); pFrame->linesize[i] = -pFrame->linesize[i]; } }