FFMPEG-初探认识-YUV转RGB用例

前段时间因为项目需要,尝试通过FFMPEG将图像帧进行格式转换,评估测试了一下,记录一下FFMPEG的一些基本信息资料


概念:

来自百科:
FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。它包括了领先的音/视频编码库libavcodec等。
libavformat:用于各种音视频封装格式的生成和解析,包括获取解码所需信息以生成解码上下文结构
和读取音视频帧等功能;
libavcodec:用于各种类型声音/图像编解码;
libavutil:包含一些公共的工具函数;
libswscale:用于视频场景比例缩放、色彩映射转换;
libpostproc:用于后期效果处理;
ffmpeg:该项目提供的一个工具,可用于格式转换、解码或电视卡即时编码等;
ffsever:一个 HTTP 多媒体即时广播串流服务器;
ffplay:是一个简单的播放器,使用ffmpeg 库解析和解码,通过SDL显示;

通俗理解为一个 音视频流处理库集合,跟之前接触过的opencv有相同之处,都涉及不深,个人感觉感觉侧重点不同,ffmpeg侧重音视频源数据流的格式编解码,opencv侧重的是对已存在视图的算法处理操作

ffmpeg 官网下载:https://ffmpeg.org/


编译:

ubuntu 32bit 下编译,目标平台android,需要配置NDK ,下载ffmpeg-3.1.4的根目录下创建一个编译脚本build_android.sh,内容如下:

#!/bin/bash
NDK=/home/xxx/tool/android-ndk-r10
SYSROOT=$NDK/platforms/android-19/arch-arm/
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86
function build_one
{
./configure \
    --prefix=$PREFIX \
    --enable-shared \
    --disable-static \
    --disable-doc \
    --disable-ffmpeg \
    --disable-ffplay \
    --disable-ffprobe \
    --disable-ffserver \
    --enable-gpl \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
    --target-os=linux \
    --arch=arm \
    --enable-cross-compile \
    --sysroot=$SYSROOT \
    --extra-cflags="-Os -fpic $ADDI_CFLAGS" \
    --extra-ldflags="$ADDI_LDFLAGS" \
    $ADDITIONAL_CONFIGURE_FLAG
#make clean
make
make install
}
CPU=arm
PREFIX=$(pwd)/android/$CPU 
ADDI_CFLAGS="-marm"
build_one

关键是NDK的路径,根据实际情况配置,native-api 的版本以及交叉编译工具链的选择,需注意 32-64 x86主机区别,目标平台为arm
按上面编译之后对应ffmpeg-3.1.4\android\arm 目录下生成 头文件以及 lib库,直接拿到android上使用


YUV格式概念:

网上的总结:
YUV。分为三个分量,“Y”表示明亮度(Luminance或Luma)。也就是灰度值;而“U”和“V” 表示的则是色度(Chrominance或Chroma),
作用是描写叙述影像色彩及饱和度,用于指定像素的颜色。

与我们熟知的RGB类似。YUV也是一种颜色编码方法,主要用于电视系统以及模拟视频领域,它将亮度信息(Y)与色彩信息(UV)分离,
没有UV信息一样能够显示完整的图像,仅仅只是是黑白的,这种设计非常好地攻克了彩色电视机与黑白电视的兼容问题。

而且,YUV不像RGB那样要求三个独立的视频信号同一时候传输,所以用YUV方式传送占用极少的频宽。

通过YUV与RGB的转换公式提取出每一个像素点的RGB值,然后显示出来。

YUV 4:4:4採样,每个Y相应一组UV分量。
YUV 4:2:2採样。每两个Y共用一组UV分量。
YUV 4:2:0採样,每四个Y共用一组UV分量。

参考网上的博客 http://blog.csdn.net/beyond_cn/article/details/12998247


YUV转RGB:

通过ffmpeg将一帧YUV格式的图像转换成成RGB格式并存储,需要用到 libavutil 以及 libswscale
网上摘的转换实现:

#include <stdio.h>

#include <utils/Log.h>

#define LOG_TAG "ffmpeg"

extern "C"
{
#include "libswscale/swscale.h"
#include "libavutil/opt.h"
#include "libavutil/imgutils.h"
};

#include <android/log.h>

#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR  , "ffmpeg", __VA_ARGS__)

int main(int argc, char* argv[])
{
    //Parameters    
    FILE *src_file =fopen("1088p_yuv420p.yuv", "rb");
    const int src_w=1920,src_h=1088;
    AVPixelFormat src_pixfmt=AV_PIX_FMT_YUV420P;

    int src_bpp=av_get_bits_per_pixel(av_pix_fmt_desc_get(src_pixfmt));

    FILE *dst_file = fopen("480p_rgb24.rgb", "wb");
    const int dst_w=800,dst_h=480;
    AVPixelFormat dst_pixfmt=AV_PIX_FMT_RGB565LE/*AV_PIX_FMT_RGB24*/;
    int dst_bpp=av_get_bits_per_pixel(av_pix_fmt_desc_get(dst_pixfmt));

    //Structures
    uint8_t *src_data[4];
    int src_linesize[4];

    uint8_t *dst_data[4];
    int dst_linesize[4];

    int rescale_method=SWS_BILINEAR/*SWS_POINT*//*SWS_BICUBIC*/;
    struct SwsContext *img_convert_ctx;
    uint8_t *temp_buffer=(uint8_t *)malloc(src_w*src_h*src_bpp/8);

    int frame_idx=0;
    int ret=0;
    ret= av_image_alloc(src_data, src_linesize,src_w, src_h, src_pixfmt, 1);
    if (ret< 0) {
        printf( "Could not allocate source image\n");
        return -1;
    }
    ret = av_image_alloc(dst_data, dst_linesize,dst_w, dst_h, dst_pixfmt, 1);
    if (ret< 0) {
        printf( "Could not allocate destination image\n");
        return -1;
    }
    //----------------------------- 
    //Init Method 1
//  img_convert_ctx =sws_alloc_context();
//  //Show AVOption
//  av_opt_show2(img_convert_ctx,stdout,AV_OPT_FLAG_VIDEO_PARAM,NULL);
//  //Set Value
//  av_opt_set_int(img_convert_ctx,"sws_flags",SWS_BICUBIC|SWS_PRINT_INFO,NULL);
//  av_opt_set_int(img_convert_ctx,"srcw",src_w,NULL);
//  av_opt_set_int(img_convert_ctx,"srch",src_h,NULL);
//  av_opt_set_int(img_convert_ctx,"src_format",src_pixfmt,NULL);
//  //'0' for MPEG (Y:0-235);'1' for JPEG (Y:0-255)
//  av_opt_set_int(img_convert_ctx,"src_range",1,NULL);
//  av_opt_set_int(img_convert_ctx,"dstw",dst_w,NULL);
//  av_opt_set_int(img_convert_ctx,"dsth",dst_h,NULL);
//  av_opt_set_int(img_convert_ctx,"dst_format",dst_pixfmt,NULL);
//  av_opt_set_int(img_convert_ctx,"dst_range",1,NULL);
//  sws_init_context(img_convert_ctx,NULL,NULL);

    //Init Method 2
    img_convert_ctx = sws_getContext(src_w, src_h,src_pixfmt, dst_w, dst_h, dst_pixfmt,
        rescale_method, NULL, NULL, NULL);
    //-----------------------------
    /*
    //Colorspace
    ret=sws_setColorspaceDetails(img_convert_ctx,sws_getCoefficients(SWS_CS_ITU601),0,
        sws_getCoefficients(SWS_CS_ITU709),0,
         0, 1 << 16, 1 << 16);
    if (ret==-1) {
        printf( "Colorspace not support.\n");
        return -1;
    }
    */
    while(1)
    {
        if (fread(temp_buffer, 1, src_w*src_h*src_bpp/8, src_file) != src_w*src_h*src_bpp/8){
            break;
        }

        switch(src_pixfmt){
        case AV_PIX_FMT_GRAY8:{
            memcpy(src_data[0],temp_buffer,src_w*src_h);
            break;
                              }
        case AV_PIX_FMT_YUV420P:{
            memcpy(src_data[0],temp_buffer,src_w*src_h);                    //Y
            memcpy(src_data[1],temp_buffer+src_w*src_h,src_w*src_h/4);      //U
            memcpy(src_data[2],temp_buffer+src_w*src_h*5/4,src_w*src_h/4);  //V
            break;
                                }
        case AV_PIX_FMT_YUV422P:{
            memcpy(src_data[0],temp_buffer,src_w*src_h);                    //Y
            memcpy(src_data[1],temp_buffer+src_w*src_h,src_w*src_h/2);      //U
            memcpy(src_data[2],temp_buffer+src_w*src_h*3/2,src_w*src_h/2);  //V
            break;
                                }
        case AV_PIX_FMT_YUV444P:{
            memcpy(src_data[0],temp_buffer,src_w*src_h);                    //Y
            memcpy(src_data[1],temp_buffer+src_w*src_h,src_w*src_h);        //U
            memcpy(src_data[2],temp_buffer+src_w*src_h*2,src_w*src_h);      //V
            break;
                                }
        case AV_PIX_FMT_YUYV422:{
            memcpy(src_data[0],temp_buffer,src_w*src_h*2);                  //Packed
            break;
                                }
        case AV_PIX_FMT_RGB24:{
            memcpy(src_data[0],temp_buffer,src_w*src_h*3);                  //Packed
            break;
                                }
        default:{
            printf("Not Support Input Pixel Format.\n");
            break;
                              }
        }

        ALOGE("-Finish process frame %5d\n",frame_idx);


        sws_scale(img_convert_ctx, src_data, src_linesize, 0, src_h, dst_data, dst_linesize);

        ALOGE("*Finish process frame %5d\n",frame_idx);


        printf("test-Finish process frame %5d\n",frame_idx);

        frame_idx++;

        switch(dst_pixfmt){
        case AV_PIX_FMT_GRAY8:{
            fwrite(dst_data[0],1,dst_w*dst_h,dst_file); 
            break;
                              }
        case AV_PIX_FMT_YUV420P:{
            fwrite(dst_data[0],1,dst_w*dst_h,dst_file);                 //Y
            fwrite(dst_data[1],1,dst_w*dst_h/4,dst_file);               //U
            fwrite(dst_data[2],1,dst_w*dst_h/4,dst_file);               //V
            break;
                                }
        case AV_PIX_FMT_YUV422P:{
            fwrite(dst_data[0],1,dst_w*dst_h,dst_file);                 //Y
            fwrite(dst_data[1],1,dst_w*dst_h/2,dst_file);               //U
            fwrite(dst_data[2],1,dst_w*dst_h/2,dst_file);               //V
            break;
                                }
        case AV_PIX_FMT_YUV444P:{
            fwrite(dst_data[0],1,dst_w*dst_h,dst_file);                 //Y
            fwrite(dst_data[1],1,dst_w*dst_h,dst_file);                 //U
            fwrite(dst_data[2],1,dst_w*dst_h,dst_file);                 //V
            break;
                                }
        case AV_PIX_FMT_YUYV422:{
            fwrite(dst_data[0],1,dst_w*dst_h*2,dst_file);               //Packed
            break;
                                }
        case AV_PIX_FMT_RGB24:{
            fwrite(dst_data[0],1,dst_w*dst_h*3,dst_file);               //Packed
            break;
                              }
        case AV_PIX_FMT_RGB565LE:{
            fwrite(dst_data[0],1,dst_w*dst_h*2,dst_file);               //Packed
            break;
                              }

        default:{
            printf("Not Support Output Pixel Format.\n");
            break;
                            }
        }
    }

    sws_freeContext(img_convert_ctx);

    free(temp_buffer);
    fclose(dst_file);
    av_freep(&src_data[0]);
    av_freep(&dst_data[0]);
    printf("ffmpeg_scale complete \n");
    return 0;
}

代码比较简单明了,需要注意的是 img_convert_ctx Init Method 方法有两种,
调用swscale中的接口 sws_scale(…)实现缩放,第一种init默认缩放算法为 SWS_BICUBIC ,第二种可以自己设定指定rescale_method

支持格式头文件:ffmpeg-3.1.4\libavutil\pixfmt.h

scale算法种类: ffmpeg-3.1.4\libswscale\swscale.h

至于算法的选择可参考 http://www.cnblogs.com/acloud/archive/2011/10/29/sws_scale.html
有具体的测试数据

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值