美文网首页
音视频基础知识及ffmpeg3.1.3解码视频文件到YUV数据

音视频基础知识及ffmpeg3.1.3解码视频文件到YUV数据

作者: ytxhao | 来源:发表于2017-07-06 17:40 被阅读159次

    音视频基础知识及ffmpeg3.1.3解码视频文件到YUV数据

    常见视频格式

    1. AVI,RMVB,MP4,FLV,MKV等等

    这里的格式代表的是封装格式

    就是把视频数据和音频数据打包成一个文件的规范

    如何查看媒体信息

    使用工具(MediaInfo)

    可以查看到的视频信息如下

    General
    Complete name                            : C:\Users\Administrator\Desktop\72a739b.mp4 :
    Format                                   : MPEG-4                                     : //封装格式例如(mkv + mka + mks -> Matroska)
    Format profile                           : Base Media / Version 2                     : //格式简介
    Codec ID                                 : mp42
    File size                                : 1.82 MiB
    Duration                                 : 19s 100ms
    Overall bit rate mode                    : Variable
    Overall bit rate                         : 799 Kbps
    Encoded date                             : UTC 2017-06-27 05:37:46
    Tagged date                              : UTC 2017-06-27 05:37:47
    
    Video
    ID                                       : 2
    Format                                   : AVC                                       : //H.264被MPEG组织称作AVC(Advanced Video Codec/先进视频编码)
    Format/Info                              : Advanced Video Codec
    Format profile                           : Main@L3
    Format settings, CABAC                   : Yes
    Format settings, ReFrames                : 2 frames                                  : //参考帧数(B帧和P帧中的预测器里所使用的之前出现的帧的数量,范围0-16,一般设2―7之间)
    Codec ID                                 : avc1
    Codec ID/Info                            : Advanced Video Coding
    Duration                                 : 19s 100ms
    Bit rate                                 : 707 Kbps
    Width                                    : 568 pixels
    Height                                   : 320 pixels
    Display aspect ratio                     : 16:9
    Rotation                                 : 90°
    Frame rate mode                          : Constant                                  : //帧率可变模式(Constant) 帧率可变模式(Variable)
    Frame rate                               : 30.000 fps
    Color space                              : YUV
    Chroma subsampling                       : 4:2:0
    Bit depth                                : 8 bits
    Scan type                                : Progressive
    Bits/(Pixel*Frame)                       : 0.130
    Stream size                              : 1.61 MiB (89%)
    Title                                    : Core Media Video
    Encoded date                             : UTC 2017-06-27 05:37:46
    Tagged date                              : UTC 2017-06-27 05:37:47
    Color range                              : Limited
    Color primaries                          : BT.709
    Transfer characteristics                 : BT.709
    Matrix coefficients                      : BT.709
    
    Audio
    ID                                       : 1
    Format                                   : AAC
    Format/Info                              : Advanced Audio Codec
    Format profile                           : LC
    Codec ID                                 : 40
    Duration                                 : 19s 98ms
    Source duration                          : 19s 156ms
    Bit rate mode                            : Variable
    Bit rate                                 : 85.7 Kbps
    Channel(s)                               : 2 channels
    Channel(s)_Original                      : 1 channel
    Channel positions                        : Front: C
    Sampling rate                            : 44.1 KHz
    Compression mode                         : Lossy                                     ://有损压缩
    Stream size                              : 200 KiB (11%)
    Source stream size                       : 200 KiB (11%)
    Title                                    : Core Media Audio
    Encoded date                             : UTC 2017-06-27 05:37:46
    Tagged date                              : UTC 2017-06-27 05:37:47
    

    AVC说明

    AVC的规格分为三等,从低到高分别为:Baseline、Main、High

    1. Baseline(最低Profile)级别支持I/P 帧,只支持无交错(Progressive)和CAVLC,一般用于低阶或需要额外容错的应用,比如视频通话、手机视频等;
    2. Main(主要Profile)级别提供I/P/B 帧,支持无交错(Progressive)和交错(Interlaced),同样提供对于CAVLC 和CABAC 的支持,用于主流消费类电子产品规格如低解码(相对而言)的mp4、便携的视频播放器、PSP和Ipod等;
    3. High(高端Profile,也叫FRExt)级别在Main的基础上增加了8x8 内部预测、自定义量化、无损视频编码和更多的YUV 格式(如4:4:4)用于广播及视频碟片存储(蓝光影片),高清电视的应用。
      AVC 的规格主要是针对兼容性的,不同的规格能在相同级别上的平台应用。
      至于Baseline@L x.x、Main@L x.x、High@L x.x形式则是在不同级别下的码流级别,数值越大码流就越大,更耗费资源。所以就码流而言High@L3.0<High@L4.0<High@L5.1。
      Codec ID:
      FOURCC:AVC1 描述:H.264 bitstream without start codes.
      FOURCC:H264 描述:H.264 bitstream with start codes.
      带有开始码的H.264视频一般是用于无线发射、有线广播或者HD-DVD中的。这些数据流的开始都有一个开始码:0x000001 或者 0x00000001.
      有开始码的H.264视频主要是存储在MP4格式的文件中的。它的数据流的开始是1、2或者4个字节表示长度数据

    视频播放原理

    视音频技术主要包含:封装技术,视频压缩编码技术以及音频压缩编码技术

    如何播放视频

    1. 播放视频要经过以下几个步骤:解协议(网络传输协议,或者本地文件协议,这里把本地文件也看做一种协议),解封装,解码视频音频,音视频同步,音视频渲染
    P7cqaVZ.jpg

    视频数据格式(RGB,YUV)

    H264

    H.264原始码流(又称为“裸流”)是由一个一个的NALU组成的。结构如下图:

    vMb77Zj.jpg

    NAL全称Network Abstract Layer, 即网络抽象层

    在H.264/AVC视频编码标准中,整个系统框架被分为了两个层面:视频编码层面(VCL)和网络抽象层面(NAL)。其中,前者负责有效表示视频数据的内容,而后者则负责格式化数据并提供头信息,以保证数据适合各种信道和存储介质上的传输。因此我们平时的每帧数据就是一个NAL单元(SPS与PPS除外)。在实际的H264数据帧中,往往帧前面带有00 00 00 01 或 00 00 01分隔符,一般来说编码器编出的首帧数据为PPS与SPS,接着为I帧……

    其中每个NALU之间通过startcode(起始码)进行分隔,起始码分成两种:0x000001(3Byte)或者0x00000001(4Byte)。如果NALU对应的Slice为一帧的开始就用0x00000001,否则就用0x000001。
    H.264码流解析的步骤就是首先从码流中搜索0x000001和0x00000001,分离出NALU;然后再分析NALU的各个字段。

    NAL nal_unit_type 为序列参数集(SPS)、图像参数集(PPS)、增强信息(SEI)不属于帧的概念。表示后面的数据信息为序列参数集(SPS)、图像参数集(PPS)、增强信息(SEI)
    NAL类型有:

    NAL_SLICE = 1 非关键帧
    NAL_SLICE_DPA = 2
    NAL_SLICE_DPB = 3
    NAL_SLICE_DPC =4
    NAL_SLICE_IDR =5 关键帧
    NAL_SEI = 6
    NAL_SPS = 7 SPS帧
    NAL_PPS = 8 PPS帧
    NAL_AUD = 9
    NAL_FILLER = 12

    ffmpeg解码视频文件简单使用

    程序执行流程图

    1WD8LKq.jpg

    开发环境是Android studio2.3.3

    以下是我使用ffmpeg3.1.3版本,解码视频文件到YUV数据的代码

    #include <jni.h>
    #include <string>
    #include <android/log.h>
    
    
    #define TAG "PLAYER-JNI"
    #define ALOG(priority, tag, fmt...) \
        __android_log_print(ANDROID_##priority, tag, fmt)
    
    #define ALOGD(...) ((void)ALOG(LOG_DEBUG, TAG, __VA_ARGS__))
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    #include <libavformat/avformat.h>
    #include <libswscale/swscale.h>
    #include <libavutil/imgutils.h>
    
    
    void *startDecodeVideo(void *ptr) {
    
        AVCodecContext *pCodecCtx;
        AVCodec *pCodec;
        AVFrame *pFrame, *pFrameYUV;
        AVPacket *packet;
        unsigned char *out_buffer_video;
        FILE *fp_yuv;
        int ret, got_picture;
        struct SwsContext *img_convert_ctx;
        int i, videoindex = -1;
        char *mFile = (char *) ptr;
        ALOGD("startDecodeVideo mFile=%s", mFile);
        AVFormatContext *pFormatCtx = avformat_alloc_context();
        av_register_all();
        if (avformat_open_input(&pFormatCtx, mFile, NULL, NULL) != 0) {
            ALOGD("Couldn't open input stream.\n");
            pthread_exit(NULL);
        }
        if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
            ALOGD("Couldn't find stream information.\n");
            return 0;
        }
        videoindex = -1;
        for (i = 0; i < pFormatCtx->nb_streams; i++)
            if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
                videoindex = i;
                break;
            }
    
        if (videoindex == -1) {
            ALOGD("Didn't find a video stream.\n");
            return NULL;
        }
    
        packet = (AVPacket *) av_malloc(sizeof(AVPacket));
    
    
        pCodecCtx = avcodec_alloc_context3(NULL);
        avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoindex]->codecpar);
    
        pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
        if (pCodec == NULL) {
            ALOGD("Codec not found.\n");
            return NULL;
        }
        pCodecCtx->codec_id = pCodec->id;
        if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
            ALOGD("Could not open codec.\n");
            return NULL;
        }
    
        pFrame = av_frame_alloc();
        pFrameYUV = av_frame_alloc();
    
        out_buffer_video = (unsigned char *) av_malloc(
                (size_t) av_image_get_buffer_size(AV_PIX_FMT_YUV420P,
                                                  pCodecCtx->width,
                                                  pCodecCtx->height, 1));
        /**
         * 也可以使用avpicture_fill方法替换av_image_get_buffer_size,avpicture_fill为它的简单封装
         *
         */
    
        /**
            int avpicture_fill(AVPicture *picture, const uint8_t *ptr,
                               enum AVPixelFormat pix_fmt, int width, int height)
            {
                return av_image_fill_arrays(picture->data, picture->linesize,
                                            ptr, pix_fmt, width, height, 1);
            }
         */
    
        av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer_video,
                             AV_PIX_FMT_YUV420P, pCodecCtx->width,
                             pCodecCtx->height, 1);
    
    
        /**
         * 可以使用avpicture_get_size替换av_image_fill_arrays,avpicture_get_size是它的简单封装
         */
    
        /**
            int avpicture_get_size(enum AVPixelFormat pix_fmt, int width, int height)
            {
                return av_image_get_buffer_size(pix_fmt, width, height, 1);
            }
         */
    
        fp_yuv = fopen("/storage/emulated/0/output.yuv", "wb+");
        ALOGD("Decode width=%d height=%d pix_fmt=%d", pCodecCtx->width, pCodecCtx->height,
              pCodecCtx->pix_fmt);
        img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
                                         pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P,
                                         SWS_BICUBIC, NULL, NULL, NULL);
    
        i = 0;
        while (av_read_frame(pFormatCtx, packet) >= 0) {
            if (packet->stream_index == videoindex) {
                //Decode
                ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
                if (ret < 0) {
                    ALOGD("Decode Error.\n");
                    return NULL;
                }
                if (got_picture) {
    
                    sws_scale(img_convert_ctx, (const uint8_t *const *) pFrame->data, pFrame->linesize,
                              0,
                              pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
    
                    ALOGD("Decode 第%d帧", i++);
                    int y_size = pCodecCtx->width * pCodecCtx->height;
                    fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv);    //Y
                    fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv);  //U
                    fwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv);  //V
                }
            }
            av_packet_unref(packet);
        }
        sws_freeContext(img_convert_ctx);
        fclose(fp_yuv);
        av_free(pFrameYUV);
        avcodec_close(pCodecCtx);
        avformat_close_input(&pFormatCtx);
        ALOGD("Decode end");
        return NULL;
    }
    
    
    JNIEXPORT void JNICALL
    Java_ican_ytx_com_ffmpegdecodesample_MainActivity_startDecodeVideo(
            JNIEnv *env,
            jobject /* this */,
            jobject assetMgr, jstring filename) {
        const char *utf8 = env->GetStringUTFChars(filename, NULL);
        char *mFile = (char *) calloc(strlen(utf8) + 1, sizeof(char *));
        memcpy(mFile, utf8, strlen(utf8));
        pthread_t mPlayer;
        pthread_create(&mPlayer, NULL, startDecodeVideo, mFile);
    }
    
    #ifdef __cplusplus
    }
    #endif
    
    
    

    代码执行完成后会在/storage/emulated/0/目录下生成output.yuv文件,你可以使用yuv播放器来播放该文件。生成路径可自行修改,以下是github源码下载地址:

    https://github.com/ytxhao/FFmpegDecodeSample.git

    参考资料:

    1.FFMPEG视音频编解码零基础学习方法
    2.AVC编码中的规格

    相关文章

      网友评论

          本文标题:音视频基础知识及ffmpeg3.1.3解码视频文件到YUV数据

          本文链接:https://www.haomeiwen.com/subject/bfxkhxtx.html