失落印迹 2024-01-06 09:58 采纳率: 50%
浏览 13
已结题

对图像格式为YUV420p数据分割问题

对图像格式为YUV420p数据分割问题
想要实现一帧YUV420p数据上半部分进行左右分割,左边数据来源于原数据从左往右截取一半,右边数据从左边100处往右截取一半
部分代码如下:
    int width = frame->width;
    int height = frame->height;
    int lsz = frame->linesize[0];//一行数据大小
    int srcOffset = lsz >> 1;
    int cpySize = srcOffset;
    int dstOffset = 100 ;
    int bufSize = width * height;
    uchar* bufTemp = new uchar[bufSize];
    //处理Y分量
    memset(bufTemp, 0, bufSize);
    memcpy(bufTemp, frame->data[0], bufSize);
    for (int row = 0; row < (height>>1); ++row)//只处理上半部分
    {
         memset(frame->data[0] + row * lsz + srcOffset,0, 1);//中间分割线
         memcpy(frame->data[0] + row * lsz + srcOffset + 1, bufTemp + row * lsz + dstOffset + 1, srcOffset - 1);
    }

运行结果却是这样如下图:

img

代码的思路就是只修改右半部分,所以每行拷贝的偏移地址是 lsz >> 1(即width/2);看上图结果好像右半部分没有拷贝完全,画面错乱了,不知道是什么情况?有没有帮忙看看是哪里的问题?

  • 写回答

2条回答 默认 最新

  • 失落印迹 2024-01-17 14:01
    关注

    经过几天的琢磨、写demo验证,终于解决问题了:

    1. 保存修改前和修改后的数据,使用雷神YUVPlayer逐帧看,发现新进来的frame->data是上一帧拆分后的数据,具体原因可能是每帧视频帧并不是完整的数据帧(关键帧-key_frame),而是每帧只保存运动(不同部分)的数据,这是视频编码压缩的一种方式;
    2. 通过这一现象作为突破口,发现每帧frame->data[0]的内存地址都是一样的,也就是说每帧的data部分都是保存在同一内存,即拆分了前一帧数据,下一帧过来是拆分后的数据,对拆分后的一帧数据再次拆分,必然造成上图的错乱的现象;
    3. 解决办法是,不修改原始frame->data的数据,重新构造一个AVFrame,再使用新构造的AVFrame显示,具体代码如下:
    AVFrame* VideoForm::CropYUVFrame(AVFrame* srcFrame)
    {
        int width = srcFrame->width;//768
        int height = srcFrame->height;    //576
        int lsz = srcFrame->linesize[0];//一行数据大小
        int srcOffset = lsz >> 1;
        int cpySize = lsz - srcOffset - 1;
        int dstOffset = lsz * 3 / 10;
    
        int YSize = width * height;
        int UVSize = YSize >> 2;
        // 构造dstframe ,注意由于不同数据帧的data都是共用一块内存的,所以不能直接修改原始帧的数据;
        // 如果直接修改原始帧数据,下一帧数据过来的并不是原始帧数据,是上一次修改后的数据基础上再修改,将造成画面错乱,所以需要重新构造目标帧
        AVFrame* dstFrame = av_frame_alloc();
        dstFrame->width = width;
        dstFrame->height = height;
        dstFrame->linesize[0] = srcFrame->linesize[0];
        dstFrame->linesize[1] = srcFrame->linesize[1];
        dstFrame->linesize[2] = srcFrame->linesize[2];
        dstFrame->format = srcFrame->format;
        //设置好宽高,linesize后再填充data
        av_frame_get_buffer(dstFrame, 1);//data内存申请
        memcpy(dstFrame->data[0], srcFrame->data[0], YSize);
        memcpy(dstFrame->data[1], srcFrame->data[1], UVSize);
        memcpy(dstFrame->data[2], srcFrame->data[2], UVSize);
    
        int dstWidth = srcOffset;
        int srcWidth = lsz - dstOffset;
        //缩放采用最近邻插值算法实现
        double scale = srcWidth * 1.0 / dstWidth;
        unsigned long* pdwSrcXTable = (unsigned long*)malloc(sizeof(unsigned long) * dstWidth);
        for (int x = 0; x < dstWidth; x++)//生成表 pdwSrcXTable 
        {
            pdwSrcXTable[x] = qRound(x * scale);//数据对齐(先除以pixelBytes取整再乘以pixelBytes)
        }
    
        //处理Y分量
        for (int row = 0; row < (height >> 1); ++row)
        {
            //memset(dstFrame->data[0] + row * lsz + srcOffset, 0, lsz - srcOffset);//中间分割线
            //memcpy(dstFrame->data[0] + row * lsz + srcOffset + 1, srcFrame->data[0] + row * lsz + dstOffset, cpySize);
            for (int col = 0; col < lsz; ++col)
            {
                if (col == srcOffset)
                {
                    dstFrame->data[0][row * lsz + col] = 0;
                }
                else if (col < srcOffset)
                {
                    dstFrame->data[0][row * lsz + col] = srcFrame->data[0][row * lsz + pdwSrcXTable[col]];
                }
                else
                {
                    dstFrame->data[0][row * lsz + col] = srcFrame->data[0][row * lsz + dstOffset + pdwSrcXTable[col - srcOffset]];
                }
            }
        }
    
        static int c = 0;
        std::string  strSrc = "./frame_src_" + std::to_string(width) + "x" + std::to_string(height) + ".yuv";
        std::string  strDst = "./frame_dst_" + std::to_string(width) + "x" + std::to_string(height) + ".yuv";
        static FILE* fp1 = fopen(strSrc.c_str(), "wb+");
        static FILE* fp2 = fopen(strDst.c_str(), "wb+");
        if (c <= 99)
        {
            fwrite(srcFrame->data[0], 1, YSize, fp1);
            fwrite(dstFrame->data[0], 1, YSize, fp2);
            c++;
        }
        else
        {
            fclose(fp1);
            fclose(fp2);
        }
    
        //处理UV分量
        lsz = srcFrame->linesize[1];
        for (int row = 0; row < (height >> 2); ++row)
        {
            //memcpy(dstFrame->data[1] + row * srcOffset + (srcOffset >> 1)
            // , srcFrame->data[1] + row * srcOffset + (dstOffset >> 1), (srcOffset >> 1));
            for (int col = 0; col < lsz; ++col)
            {
                if (col < (lsz >> 1))
                {
                    dstFrame->data[1][row * lsz + col] = srcFrame->data[1][row * lsz + pdwSrcXTable[col]];
                    dstFrame->data[2][row * lsz + col] = srcFrame->data[2][row * lsz + pdwSrcXTable[col]];
                }
                else
                {
                    dstFrame->data[1][row * lsz + col] = srcFrame->data[1][row * lsz + (dstOffset >> 1) + pdwSrcXTable[col - (lsz >> 1)]];
                    dstFrame->data[2][row * lsz + col] = srcFrame->data[2][row * lsz + (dstOffset >> 1) + pdwSrcXTable[col - (lsz >> 1)]];
                }
            }
        }
    
        free(pdwSrcXTable);
        return dstFrame;
    }
    
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 1月17日
  • 已采纳回答 1月17日
  • 修改了问题 1月6日
  • 创建了问题 1月6日

悬赏问题

  • ¥20 求个正点原子stm32f407开发版的贪吃蛇游戏
  • ¥15 正弦信号发生器串并联电路电阻无法保持同步怎么办
  • ¥15 划分vlan后,链路不通了?
  • ¥20 求各位懂行的人,注册表能不能看到usb使用得具体信息,干了什么,传输了什么数据
  • ¥15 个人网站被恶意大量访问,怎么办
  • ¥15 Vue3 大型图片数据拖动排序
  • ¥15 Centos / PETGEM
  • ¥15 划分vlan后不通了
  • ¥20 用雷电模拟器安装百达屋apk一直闪退
  • ¥15 算能科技20240506咨询(拒绝大模型回答)