使用FFmpeg进行yuv420转rgba

发布时间 2023-08-15 11:03:47作者: DoubleLi

讲解一下将获取到视频数据,进行rgb转码,并且进行相应的缩放操作

// 存放解码过后的数据
unsigned char *decode_data;
int decode_size = 0;

/**
 * 解码AVFrame中的yuv420数据并且转换为rgba数据
 *
 * @param frame 需要解码的帧结构
 * @param src_width 需要转换的帧宽度
 * @param src_height 需要转换的帧高度
 * @param src_pix_fmt 需要转换的帧编码方式
 * @param dst_width 转换后目标的宽度
 * @param dst_height 转换后目标的高度
 * @return
 *
 **/
int decode_frame(AVFrame *frame, int src_width, int src_height,
                 AVPixelFormat src_pix_fmt, int dst_width, int dst_height) {
    struct SwsContext *pSwsCtx;
    // 转换后的帧结构对象
    AVFrame *dst_frameRGBA = NULL;
    // 目标帧结构初始化
    dst_frameRGBA = av_frame_alloc();

    BYTE *outBuff = NULL;
    // 初始化目标帧长度
    int dst_frame_size;
    // 计算RGBA下的目标长度
    dst_frame_size = avpicture_get_size(AV_PIX_FMT_RGBA, dst_width, dst_height);
    // 分配转换后输出的内存空间
    outBuff = (uint8_t *) av_malloc(dst_frame_size);
    // 初始化目标帧
    avpicture_fill((AVPicture *) dst_frameRGBA, outBuff, AV_PIX_FMT_RGBA,
                   dst_width, dst_height);

    // 获取缩放上下文
    pSwsCtx = sws_getContext(src_width, src_height, src_pix_fmt, dst_width, dst_height, AV_PIX_FMT_RGBA,
                             SWS_BICUBIC, NULL, NULL, NULL);
    // 缩放,结果保存在目标帧结构的dst_frameRGBA->data中
    sws_scale(pSwsCtx, frame->data,
              frame->linesize, 0, src_height,
              dst_frameRGBA->data, dst_frameRGBA->linesize);

    // 存储帧结果
    if (decode_data == NULL) {
        decode_data = malloc(dst_frame_size * sizeof(char));
        // 测试保存成文件
//        save_frame(frame, src_width, src_height);
//        save_rgb(dst_frameRGBA->data[0], dst_frame_size);
    } else if (decode_size != dst_frame_size * sizeof(char)) {
        // 如果解码过后的数据大小不满足解码区域,那么重新分配内存
        decode_data = realloc(decode_data, dst_frame_size * sizeof(char));
    }

    // 将解码后的数据拷贝到decode_data中
    memcpy(decode_data, dst_frameRGBA->data[0], dst_frame_size * sizeof(char));
    // 计算解码后的帧大小
    decode_size = dst_frame_size * sizeof(char);
    // 释放相关内容
    av_free(outBuff);
    av_free(dst_frameRGBA);
    return 1;
}

/**
 * 保存rgb数据
 * @param data
 * @param frameSize
 * */
void save_rgb(uint8_t *data, int frameSize) {

    FILE *pFile;
    char *szFilename = "/sdcard/RGB";
    int y;

    pFile = fopen(szFilename, "wb");

    if (pFile == NULL)
        return;

    //写入文件
    fwrite(data, 1, frameSize, pFile);

    // Close file
    fclose(pFile);

}

/**
 * 保存yuv数据
 * @param data
 * @param frameSize
 * */
void save_frame(AVFrame *pFrame, int width, int height) {

    FILE *pFile;
    char *szFilename = "/sdcard/frame.yuv";
    int y;

    pFile = fopen(szFilename, "wb");

    if (pFile == NULL)
        return;

    int y_size = width * height;
    int u_size = y_size / 4;
    int v_size = y_size / 4;

    //写入文件
    //首先写入Y,再是U,再是V
    //in_frame_picture->data[0]表示Y
    fwrite(pFrame->data[0], 1, y_size, pFile);
    //in_frame_picture->data[1]表示U
    fwrite(pFrame->data[1], 1, u_size, pFile);
    //in_frame_picture->data[2]表示V
    fwrite(pFrame->data[2], 1, v_size, pFile);

    // Close file
    fclose(pFile);

}

查看测试文件结果,windows下可以使用RawViewer,OSX可以使用YUView


 

OSX下:


 

注意由于我们是直接将数据保存在文件中,需要在YUView或者RawViewer中手动设置width和height属性。好了,这节就讲到这里,有什么问题,欢迎交流~~~