美文网首页
FFmpeg学习

FFmpeg学习

作者: 一枚懒人 | 来源:发表于2021-06-09 22:14 被阅读0次

    FFmpeg总结

        FFmpeg 开源音视频库,主要用来处理视频数据等,可以编码,解码,缩放,音频重采样等功能
    

    1:mp4文件抽取H264文件

    2:H264文件抽取YUV文件

    抽取出yuv数据主要包含3步

    1:首先将mp4文件中的H264码流文件抽取出来

    2:将h264文件送进去解码

    3:得到解码后的每一帧数据,进行保存

    上述3步的步骤代码详细如下:

    首先是将对后续要使用的ffmpeg API 和对象初始化

    // 初始化基本参数,主要包括
    // avpacket 解码前数据
    // avCodec AVCodec指针对象
    // avCodecParserContext AVCodecParserContext指针对象,主要
    // avCodecContext AVCodecContext指针对象 编码器codec的上下文
    // avFrame AVFrame 指针对象 存放解码后数据
    avPacket = av_packet_alloc();
        if (!avPacket) {
            LOGE("AVPACKET open failed");
            goto end;
        }
        memset(input + INBUF_SIZE_NEW, 0, AV_INPUT_BUFFER_PADDING_SIZE);
        avCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
        if (!avCodec) {
            LOGE("avcodec_find_decoder  open failed");
            goto end;
        }
        LOGE("avcodec_find_decoder  open sucess");
        avCodecParserContext = av_parser_init(avCodec->id);
        if (!avCodecParserContext) {
            LOGE("avCodecParserContext  init failed");
            goto end;
        }
        LOGE("av_parser_init  open sucess");
        avCodecContext = avcodec_alloc_context3(avCodec);
    //    av_find_best_stream(avFormatContext,AVMEDIA_TYPE_VIDEO,)
    
        if (!avCodecContext) {
            LOGE("avcodec_alloc_context3 failed");
            goto end;
        }
        LOGE("avcodec_alloc_context3  open sucess");
        result = avcodec_open2(avCodecContext, avCodec, nullptr);
        if (result < 0) {
            LOGE("avcodec_open2 failed");
            goto end;
        }
        LOGE("avcodec_open2  open sucess");
        file = fopen(src_file, "rbe");
        if (!file) {
            LOGE("open file failed, error is %s:", strerror(errno));
        }
    
        LOGE("fopen  open sucess");
        avFrame = av_frame_alloc();
        if (!avFrame) {
            LOGE("avframe alloc failed");
            goto end;
        }
    

    第二步将文件读取的数据进行读取倒avpacket中

    while (!feof(file)) {
        //每次从文件中读取INBUF_SIZE_NEW + AV_INPUT_BUFFER_PADDING_SIZE = 4096 +64 个大小的数据
        data_size = fread(input, 1, INBUF_SIZE_NEW, file);
        if (!data_size) {
            break;
        }
        LOGE("fread  sucess :%d ", data_size);
        data = input;
        while (data_size > 0) {
            //将4096 +64  个数据中读取到result个数据到avPacket 
            result = av_parser_parse2(avCodecParserContext, avCodecContext, &avPacket->data,
                                      &avPacket->size, data, data_size, AV_NOPTS_VALUE,
                                      AV_NOPTS_VALUE, 0);
            LOGE("av_parser_parse2  open sucess");
            if (result < 0) {
                LOGE("av_parser_parse2 failed");
                goto end;;
            }
            //指针后移
            data += result;
            data_size -= result;
            //数据送去解码,并保存成yuv文件
            if (avPacket->size) {
                decode(avCodecContext, avFrame, avPacket, des_file);
            }
            LOGE("decode   sucess");
        }
    }
    decode(avCodecContext, avFrame, nullptr, des_file);
    

    将avpacket数据送到解码器中解码

    static void decode(AVCodecContext *avCodecContext, AVFrame *avFrame, AVPacket *pkt,
                       const char *filename) {
        char buf[1024];
        int ret;
    
        //将packet发送给codec
        ret = avcodec_send_packet(avCodecContext, pkt);
        if (ret < 0) {
            __android_log_print(ANDROID_LOG_ERROR, TAG_ZHF, "Error sending a packet for decoding: %s\n",
                                av_err2str(ret));
            return;
        }
    
        while (ret >= 0) {
            //解码出frame并存入avFrame参数
            ret = avcodec_receive_frame(avCodecContext, avFrame);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                return;
            } else if (ret < 0) {
                __android_log_write(ANDROID_LOG_ERROR, TAG_ZHF, "Error during decoding\n");
                return;
            }
    
            //为防止文件太多观察不便,每20个avFrame中抽取一个并保存为文件
            if (avCodecContext->frame_number % 20 == 0) {
                __android_log_print(ANDROID_LOG_ERROR, TAG_ZHF, "saving avFrame %3d\n",
                                    avCodecContext->frame_number);
    
                /* the picture is allocated by the decoder. no need to
                   free it */
                //拼接文件名
                //C库函数:int snprintf(char *str, size_t size, const char *format, ...),将可变参数(...)按照format格式化成字符串,
                //并将字符串复制到str中,size为要写入的字符的最大数目,超过size会被截断。
                snprintf(buf, sizeof(buf), "%s-%d.yuv", filename, avCodecContext->frame_number);
                yuv_save(avFrame, buf);
            }
        }
    }
    

    将数据保存成yuv文件

    tatic void yuv_save(AVFrame *avFrame, char *filename) {
        FILE *file;
    
        file = fopen(filename, "we");
        if (!file) {
            __android_log_write(ANDROID_LOG_ERROR, TAG_ZHF, "Could not open out file\n");
            __android_log_write(ANDROID_LOG_ERROR, TAG_ZHF, strerror(errno));
            return;
        }
        int width = avFrame->width;
        int height = avFrame->height;
        for (int i = 0; i < height; i++)
            fwrite(avFrame->data[0] + i * avFrame->linesize[0], 1, width, file);
        for (int j = 0; j < height / 2; j++)
            fwrite(avFrame->data[1] + j * avFrame->linesize[1], 1, width / 2, file);
        for (int k = 0; k < height / 2; k++)
            fwrite(avFrame->data[2] + k * avFrame->linesize[2], 1, width / 2, file);
    
        fclose(file);
    }
    
    yuv文件展示.png

    详细代码参考github:

    代码传送门

    相关文章

      网友评论

          本文标题:FFmpeg学习

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