专栏首页曾大稳的博客ffmpeg android视频解码

ffmpeg android视频解码

解码流程:

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

(图片来源于网络)

#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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android FFmpeg 音视频解码播放(十五)

    通常情况下,媒体文件以如MP4,MKV、FLV等等格式存在我们的计算机,手机等设备中,而这些文件格式都属于封装格式,就是把音视频数据按照相应的规范,打包成文件。

    PengJie
  • 【Android 音视频开发:FFmpeg音视频编解码篇】三、Android FFmpeg视频解码播放

    本文很长,因为可能有比较多的小伙伴对 JNI C/C++ 不是很熟悉,所以本文比较详细的对 FFmpeg 用到的代码进行讲解,完整的演示了一遍 FFmpeg 的...

    开发的猫
  • 【Android 音视频开发:FFmpeg音视频编解码篇】二、Android 引入FFmpeg

    在过去,通常使用 makefile 的方式在项目中引入 C/C++ 代码支持,随着 Android Studio 的普及,makefile 的方式已经基本被 C...

    开发的猫
  • 【Android 音视频开发打怪升级:FFmpeg音视频编解码篇】七、Android FFmpeg 视频编码

    本文是音视频系列文章的最后一篇了,也是拖了最久的一篇(懒癌发作-_-!!),终于下定决心,把坑填完。

    开发的猫
  • FFmpeg 开发(01):FFmpeg 编译和集成

    FFmpeg 是一款知名的开源音视频处理软件,它提供了丰富而友好的接口支持开发者进行二次开发。

    字节流动
  • ffmpeg android音频解码

    音频解码就是将mp3 aac等格式这些文件解析为pcm格式的过程。 和视频解码流程一样,只是有些函数不一样

    曾大稳
  • 全网最全的 Android 音视频和 OpenGL ES 干货,都在这了

    有位大佬说,“这是全网最全的 Android OpenGL ES 教程”,哈哈,对于这种善意的“商业互吹”,当然是欣然接受,这无疑给了我更多的动力和激情来完善这...

    字节流动
  • 音视频学习路线(二)

    本文主要讲一下笔者计划在音视频方向的学习路线计划,主要以Android开发为例,让我们一起进步。

    PengJie
  • FFmpeg、x264以及fdk-aac 编译整合

    最近在根据项目需求疯狂撸 OpenCL ,FFmpeg 相关的文章落下了不少,后面也准备介绍下 OpenCL 在 Android 上的应用,另外 OpenCL ...

    字节流动
  • 【Android 音视频开发打怪升级:FFmpeg音视频编解码篇】六、FFmpeg简单合成MP4:视屏解封与重新封装

    前面的文章中,对 FFmpg 视频的解码,以及如何利用 OpenGL 对视频进行编辑和渲染,做了详细的讲解,接来非常重要的,就是对编辑好的视频进行编码和保存。

    开发的猫
  • 【Android 音视频开发打怪升级:FFmpeg音视频编解码篇】四、Android FFmpeg+OpenSL ES音频解码播放

    在上篇文章中,详细介绍了 FFmepg 的播放流程,以及抽象了解码流程框架,整合视频和音频解码流程的共同点,形成了 BaseDecoder 类。通过继承 Bas...

    开发的猫
  • 【Android 音视频开发打怪升级:FFmpeg音视频编解码篇】一、FFmpeg so库编译

    网上其实已经有很多的关于FFmpeg so库编译的分享,但是大部分都是直接把配置文件的内容贴出来。我想大部分取搜索 「如何编译FFmpeg so库」的人,对交叉...

    开发的猫
  • Android本地视频压缩方案的示例代码

    本文讨论的不是类似秒拍的短视频录制,而是用户选择本地一个现有视频,压缩后上传。秒拍的实现其实是自定义视频录制功能,从而控制录制时长,分辨率,码率等,生成体积很小...

    砸漏
  • 从零开始仿写一个抖音App——基于FFmpeg的极简视频播放器GitHub地址

    本文首发于简书——何时夕,搬运转载请注明出处,否则将追究版权责任。交流qq群:859640274

    何时夕
  • FFmpeg 视频录制 - 视频添加滤镜和编码

    音视频开发中,视频编码是另一个重要的部分,基于 FFmpeg 软件解码前面系列文章已经介绍过了。

    字节流动
  • Ijkplayer、ExoPlayer、VLC播放器综合比较

    VLC 是VideoLAN 计划所研发的工程,最早预1996年开始,是一个完全的跨平台播放器,适合Windows、Mac OS、Linux、Android、iO...

    马上就说
  • 音视频领域火爆的开源项目

    ffmpeg应该是音视频入门必须要掌握的开源项目,涉及到音视频从生产到消费的完整过程,ffmpeg是一个综合性项目,涉及到非常多的知识点;

    马上就说
  • 音视频技术(1)- 参考资料

    1. 音视频开发进阶指南(笔者主要从事移动端开发,以这本书入门,通篇了解音视频处理比较合适):

    sumsmile
  • 基于 ffmpeg 的跨平台播放器实现

    随着游戏娱乐等直播业务的增长,在移动端观看直播的需求也日益迫切。但是移动端原生的播放器对各种直播流的支持却不是很好。Android 原生的 MediaPlaye...

    许斌盛

扫码关注云+社区

领取腾讯云代金券