前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ffmpeg android视频解码

ffmpeg android视频解码

作者头像
曾大稳
发布2018-09-11 10:54:18
1.3K0
发布2018-09-11 10:54:18
举报
文章被收录于专栏:曾大稳的博客曾大稳的博客

解码流程:

  1. 获取文件信息,数据存储在AVFormatContext里面
  2. 根据AVFormatContext获取对应的AVCodecContext
  3. 解码原始数据AVPacket,解码为自己需要的数据AVFrame
  4. 释放相关资源

(图片来源于网络)

代码语言:javascript
复制
#include "lang.h"
#include <string>
//封装格式
//解码
#include "log.h"

extern "C" {
#include <libavutil/imgutils.h>
#include <libavutil/samplefmt.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>

};


static void video_decode_example(const char *outfilename, const char *filename) {
    //1.注册
    av_register_all();

    AVFormatContext *pFormatCtx = NULL;
    //2. 打开文件
    if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0) {
        LOGE ("打开文件失败");
        return;
    }
    //3. 获取流信息,数据封装在AVFormatContext里面
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        LOGE ("获取流信息失败");
        return;
    }
    //只输出输入文件的格式信息
    av_dump_format(pFormatCtx, 0, filename, 0);
    int video_index = -1;
    //4. 从流中遍历获取video的index
    for (int i = 0; i < pFormatCtx->nb_streams; i++) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_index = i;
            LOGE ("video_index = %d", video_index);
            break;
        }
    }
    if (video_index == -1) {
        LOGE ("遍历获取video_index失败");
        return;
    }
    AVCodecContext *pCodecCtxOrg = NULL;
    AVCodecContext *pCodecCtx = NULL;

    AVCodec *pCodec = NULL;
    //5. 解码器获取
    //5.1 根据video_index获取解码器上下文AVCodecContext
    pCodecCtxOrg = pFormatCtx->streams[video_index]->codec; // codec context
    //5.1 根据AVCodecContext获取解码器
    pCodec = avcodec_find_decoder(pCodecCtxOrg->codec_id);

    if (!pCodec) {
        LOGE ("解码器获取失败");
        return;
    }

    //6.获取一个AVCodecContext实例,并将第五步获取的AVCodecContext数据copy过来,解码的时候需要用这个
    pCodecCtx = avcodec_alloc_context3(pCodec);
    if (avcodec_copy_context(pCodecCtx, pCodecCtxOrg) != 0) {
        LOGE ("解码器上下文数据copy失败");
        return;
    }
    //7. 打开解码器
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        LOGE ("打开解码器失败");
        return;
    }
    //原始数据帧
    AVFrame *pFrame = NULL;
    //yuv数据帧
    AVFrame *pFrameYUV = NULL;
    //内存开辟 不要忘记free
    pFrame = av_frame_alloc();
    pFrameYUV = av_frame_alloc();
    int numBytes = 0;
    uint8_t *buffer = NULL;
    //根据需要解码的类型,获取需要的buffer,不要忘记free
    numBytes = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height, 1);
    buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));

    //根据指定的图像参数和提供的数组设置数据指针和行数 ,数据填充到对应的pFrameYUV里面
    av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, buffer, AV_PIX_FMT_YUV420P,
                         pCodecCtx->width,
                         pCodecCtx->height, 1);

    //获取SwsContext
    struct SwsContext *sws_ctx = NULL;
    sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
                             pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC,
                             NULL, NULL, NULL);

    FILE *pFile = fopen(outfilename, "wb+");
    int ret;
    AVPacket packet;
    int frameFinished = 0;
    //8. 根据AVFormatContext 读取帧数据,读取的编码数据存储到AVPacket里面
    while (av_read_frame(pFormatCtx, &packet) >= 0) {
        if (packet.stream_index == video_index) {
            //9. 将读取到的AVPacket,转换为AVFrame
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
            if (ret < 0) {
                LOGE("解码失败");
                return;
            }
            if (frameFinished) {
                //10. 将原始的AVFrame数据转换为自己需要的YUV AVFrame数据
                sws_scale(sws_ctx, (uint8_t const *const *) pFrame->data, pFrame->linesize, 0,
                          pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
                //11. 根据YUV AVFrame数据保存文件
                if (pFile == NULL)
                    return;
                int y_size = pCodecCtx->width * pCodecCtx->height;

                //yuv420 存储为4:1:1
                fwrite(pFrame->data[0], 1, static_cast<size_t>(y_size), pFile); //y
                fwrite(pFrame->data[1], 1, static_cast<size_t>(y_size / 4), pFile);//u
                fwrite(pFrame->data[2], 1, static_cast<size_t>(y_size / 4), pFile);//v
            }
        }
        av_packet_unref(&packet);
    }

    //flush decoder
    //FIX: Flush Frames remained in Codec
    //12. 刷新解码器
    while (1) {
        ret = avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
        if (ret < 0)
            break;
        if (!frameFinished)
            break;
        sws_scale(sws_ctx, (const unsigned char *const *) pFrame->data, pFrame->linesize, 0,
                  pCodecCtx->height,
                  pFrameYUV->data, pFrameYUV->linesize);

        int y_size = pCodecCtx->width * pCodecCtx->height;
        fwrite(pFrameYUV->data[0], 1, static_cast<size_t>(y_size), pFile);    //Y
        fwrite(pFrameYUV->data[1], 1, static_cast<size_t>(y_size / 4), pFile);  //U
        fwrite(pFrameYUV->data[2], 1, static_cast<size_t>(y_size / 4), pFile);  //V
        LOGE("Flush Decoder: Succeed to decode 1 frame!\n");
    }
    //release resource
    sws_freeContext(sws_ctx);
    fclose(pFile);
    av_free(buffer);
    av_frame_free(&pFrameYUV);
    av_frame_free(&pFrame);
    avcodec_close(pCodecCtx);
    avcodec_close(pCodecCtxOrg);
    avformat_close_input(&pFormatCtx);

}


extern "C"
JNIEXPORT jint JNICALL
Java_zzw_com_ffmpegdemo_VideoUtils_decode(JNIEnv *env, jclass type, jstring input_,
                                          jstring output_) {
    const char *input_file_name = env->GetStringUTFChars(input_, 0);
    const char *output_file_name = env->GetStringUTFChars(output_, 0);
    
    video_decode_example(output_file_name, input_file_name);
    
    env->ReleaseStringUTFChars(input_, input_file_name);
    env->ReleaseStringUTFChars(output_, output_file_name);
    return 0;

}

总结:

要解码,我们需要获取解码器AVCodec,解码器我们需要通过codec_id获取,codec_id我们需要通过AVStream获取,AVStream我们需要通过AVCodecContext获取,AVCodecContext我们要根据AVFormatContext获取,解码的时候我们要通过AVFormatContext读取,解码数据存储在AVFrame里面,编码数据存储在AVPacket里面。

参考: https://blog.csdn.net/leixiaohua1020/article/details/46889389 https://blog.csdn.net/u011913612/article/details/53419986

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-05-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档