【Android FFMPEG 开发】FFMPEG 获取编解码器 ( 获取编解码参数 | 查找编解码器 | 获取编解码器上下文 | 设置上下文参数 | 打开编解码器 )

简介: 【Android FFMPEG 开发】FFMPEG 获取编解码器 ( 获取编解码参数 | 查找编解码器 | 获取编解码器上下文 | 设置上下文参数 | 打开编解码器 )

文章目录

博客简介 . FFMPEG 编解码器获取流程

I . FFMPEG 获取音视频流的编解码参数 AVCodecParameters *codecpar

II . FFMPEG 查找解码器 avcodec_find_decoder ( )

III . FFMPEG 获取编解码器上下文 avcodec_alloc_context3 ( )

IV . FFMPEG 设置编解码器上下文参数 avcodec_parameters_to_context ( )

V . FFMPEG 打开编解码器 avcodec_open2 ( )

VI . FFMPEG 获取编解码器 代码示例



博客简介 . FFMPEG 编解码器获取流程


FFMPEG 编解码器获取流程 : 在获取音视频流 AVStream *stream 之后 , 执行以下流程 ;



〇 获取 AVStream * 音视频流 ( 获取编解码器前提 ) : 参考博客 【Android FFMPEG 开发】FFMPEG 获取 AVStream 音视频流 ( AVFormatContext 结构体 | 获取音视频流信息 | 获取音视频流个数 | 获取音视频流 )



① 获取音视频流的编码参数 : AVStream *stream 结构体的 AVCodecParameters *codecpar 元素是音视频流的编解码参数 ; 包含 码率 , 宽度 , 高度 , 采样率 等参数信息 ;


//解码这个媒体流的参数信息 , 包含 码率 , 宽度 , 高度 , 采样率 等参数信息
AVCodecParameters *codecParameters = stream->codecpar;



② 查找编解码器 : 调用 avcodec_find_decoder ( ) 获取当前音视频流使用的编解码器 ;


//① 查找 当前流 使用的编码方式 , 进而查找编解码器 ( 可能失败 , 不支持的解码方式 )
AVCodec *avCodec = avcodec_find_decoder(codecParameters->codec_id);



③ 获取编解码器上下文 : 调用 avcodec_alloc_context3 ( ) 方法 , 获取编解码器上下文 ;


//② 获取编解码器上下文
AVCodecContext *avCodecContext = avcodec_alloc_context3(avCodec);



④ 设置编解码器上下文参数 : 调用 avcodec_parameters_to_context ( ) 方法 , 设置编解码器的上下文参数 ;


//③ 设置 编解码器上下文 参数
//      int avcodec_parameters_to_context(AVCodecContext *codec,
//              const AVCodecParameters *par);
//      返回值 > 0 成功 , < 0 失败
int parameters_to_context_result =
        avcodec_parameters_to_context(avCodecContext, codecParameters);



⑤ 打开编解码器 : 调用 avcodec_open2 ( ) 方法 , 打开编解码器 ;


//④ 打开编解码器
//   int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, 
//   返回 0 成功 , 其它失败
int open_codec_result = avcodec_open2(avCodecContext, avCodec, 0);



I . FFMPEG 获取音视频流的编解码参数 AVCodecParameters *codecpar


1 . 编解码参数封装在 AVStream 结构体中 : FFMPEG 音视频流的编码参数 AVCodecParameters *codecpar 是 AVStream 结构体的元素 ;


/**
 * Stream structure.
 * New fields can be added to the end with minor version bumps.
 * Removal, reordering and changes to existing fields require a major
 * version bump.
 * sizeof(AVStream) must not be used outside libav*.
 */
typedef struct AVStream {
  ...
  /**
     * Codec parameters associated with this stream. Allocated and freed by
     * libavformat in avformat_new_stream() and avformat_free_context()
     * respectively.
     *
     * - demuxing: filled by libavformat on stream creation or in
     *             avformat_find_stream_info()
     * - muxing: filled by the caller before avformat_write_header()
     */
    AVCodecParameters *codecpar;
  ...
}AVStream;



2 . FFMPEG 获取音视频流的编码参数 示例 : 其中 AVStream *stream 是之前获取的音视频流结构体指针 ;


//解码这个媒体流的参数信息 , 包含 码率 , 宽度 , 高度 , 采样率 等参数信息
AVCodecParameters *codecParameters = stream->codecpar;


3 . 编解码参数 AVCodecParameters *codecpar 中封装的数据 : 这里只列举我们需要使用的 ;


① enum AVCodecID codec_id : 编解码器使用的编码数据的特定类型 , 需要使用该值获取解码器 ;


/**
 * This struct describes the properties of an encoded stream.
 *
 * sizeof(AVCodecParameters) is not a part of the public ABI, this struct must
 * be allocated with avcodec_parameters_alloc() and freed with
 * avcodec_parameters_free().
 */
typedef struct AVCodecParameters{
  ...
    /**
     * Specific type of the encoded data (the codec used).
     */
    enum AVCodecID   codec_id;
    ...
}AVCodecParameters;



II . FFMPEG 查找解码器 avcodec_find_decoder ( )


1 . avcodec_find_decoder ( ) 函数原型 : 根据编解码器 ID , 查找被注册的解码器 ;



① enum AVCodecID id 参数 : 代表了一个编码数据的特定类型 ; ( 详情查看 I . 3 . ① 小节内容 )


② AVCodec *avCodec 返回值 : 返回值是 AVCodec * 结构体指针类型变量 , 代表了获取的解码器 ;


/**  avcodec.h
 * Find a registered decoder with a matching codec ID.
 *
 * @param id AVCodecID of the requested decoder
 * @return A decoder if one was found, NULL otherwise.
 */
AVCodec *avcodec_find_decoder(enum AVCodecID id



2 . FFMPEG 查找解码器 avcodec_find_decoder ( ) 使用示例 :


//① 查找 当前流 使用的编码方式 , 进而查找编解码器 ( 可能失败 , 不支持的解码方式 )
AVCodec *avCodec = avcodec_find_decoder(codecParameters->codec_id);
//查找失败处理
if(avCodec == NULL){
    //如果没有找到编解码器 , 回调失败 , 方法直接返回 , 后续代码不执行
    callHelper->onError(pid, 2);
    __android_log_print(ANDROID_LOG_ERROR , "FFMPEG" , "查找 编解码器 失败");
    return;
}



III . FFMPEG 获取编解码器上下文 avcodec_alloc_context3 ( )


avcodec_alloc_context3 ( ) 函数原型 : 获取编解码器上下文 ;



① const AVCodec *codec 参数 : 要获取上下文的编解码器 ;


② AVCodecContext *avCodecContext 返回值 : 编解码器上下文 , 封装了很多编解码器相关参数 ; 如果为 NULL , 说明获取失败 ;


/**  avcodec.h
 * Allocate an AVCodecContext and set its fields to default values. The
 * resulting struct should be freed with avcodec_free_context().
 *
 * @param codec if non-NULL, allocate private data and initialize defaults
 *              for the given codec. It is illegal to then call avcodec_open2()
 *              with a different codec.
 *              If NULL, then the codec-specific defaults won't be initialized,
 *              which may result in suboptimal default settings (this is
 *              important mainly for encoders, e.g. libx264).
 *
 * @return An AVCodecContext filled with default values or NULL on failure.
 */
AVCodecContext *avcodec_alloc_context3(const AVCodec *codec);



2 . FFMPEG 获取编解码器上下文 avcodec_alloc_context3 ( ) 使用示例 :


//② 获取编解码器上下文
AVCodecContext *avCodecContext = avcodec_alloc_context3(avCodec);
//获取编解码器失败处理
if(avCodecContext == NULL){
    callHelper->onError(pid, 3);
    __android_log_print(ANDROID_LOG_ERROR , "FFMPEG" , "创建编解码器上下文 失败");
    return;
}




IV . FFMPEG 设置编解码器上下文参数 avcodec_parameters_to_context ( )


1 . avcodec_parameters_to_context ( ) 函数原型 : 基于编解码器提供的编解码参数设置编解码器上下文参数 ;



① AVCodecContext *codec 参数 : 要设置参数的编解码器上下文 , 这里当做返回值使用 , 这个值之后还要使用 ;


② const AVCodecParameters *par 参数 : 媒体流的参数信息 , 包含 码率 , 宽度 , 高度 , 采样率 等参数信息 , 是 AVStream 结构体封装的元素 ;


③ 返回值 : 如果返回值 >= 0 , 说明设置编解码器上下文参数操作成功 , 如果 < 0 , 设置失败 ;


/**
 * Fill the codec context based on the values from the supplied codec
 * parameters. Any allocated fields in codec that have a corresponding field in
 * par are freed and replaced with duplicates of the corresponding field in par.
 * Fields in codec that do not have a counterpart in par are not touched.
 *
 * @return >= 0 on success, a negative AVERROR code on failure.
 */
int avcodec_parameters_to_context(AVCodecContext *codec,
                                  const AVCodecParameters *par);


2 . FFMPEG 设置编解码器上下文参数 avcodec_parameters_to_context ( ) 使用示例 :


//③ 设置 编解码器上下文 参数
//      int avcodec_parameters_to_context(AVCodecContext *codec,
//              const AVCodecParameters *par);
//      返回值 > 0 成功 , < 0 失败
int parameters_to_context_result =
        avcodec_parameters_to_context(avCodecContext, codecParameters);
//设置 编解码器上下文 参数 失败处理
if(parameters_to_context_result < 0){
    callHelper->onError(pid, 4);
    __android_log_print(ANDROID_LOG_ERROR , "FFMPEG" , "设置编解码器上下文参数 失败");
    return;
}



V . FFMPEG 打开编解码器 avcodec_open2 ( )


1 . avcodec_open2 ( ) 函数原型 : 打开编解码器 , 之前必须先初始化编解码器上下文 AVCodecContext ;



① AVCodecContext *avctx 参数 : 之前根据编解码器获取上下文参数 avcodec_alloc_context3(avCodec) , 并为其设置了音视频流编解码参数 avcodec_parameters_to_context(avCodecContext, codecParameters) ;


② const AVCodec *codec 参数 : 需要打开的编解码上下文对应的编解码器 ;


③ 返回值 : 返回 0 成功 , 其它值 失败 ;


/**
 * Initialize the AVCodecContext to use the given AVCodec. Prior to using this
 * function the context has to be allocated with avcodec_alloc_context3().
 *
 * The functions avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(),
 * avcodec_find_decoder() and avcodec_find_encoder() provide an easy way for
 * retrieving a codec.
 *
 * @warning This function is not thread safe!
 *
 * @note Always call this function before using decoding routines (such as
 * @ref avcodec_receive_frame()).
 *
 * @code
 * avcodec_register_all();
 * av_dict_set(&opts, "b", "2.5M", 0);
 * codec = avcodec_find_decoder(AV_CODEC_ID_H264);
 * if (!codec)
 *     exit(1);
 *
 * context = avcodec_alloc_context3(codec);
 *
 * if (avcodec_open2(context, codec, opts) < 0)
 *     exit(1);
 * @endcode
 *
 * @param avctx The context to initialize.
 * @param codec The codec to open this context for. If a non-NULL codec has been
 *              previously passed to avcodec_alloc_context3() or
 *              for this context, then this parameter MUST be either NULL or
 *              equal to the previously passed codec.
 * @param options A dictionary filled with AVCodecContext and codec-private options.
 *                On return this object will be filled with options that were not found.
 *
 * @return zero on success, a negative value on error
 * @see avcodec_alloc_context3(), avcodec_find_decoder(), avcodec_find_encoder(),
 *      av_dict_set(), av_opt_find().
 */
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);



2 . FFMPEG 打开编解码器 avcodec_open2 ( ) 使用示例 :


//④ 打开编解码器
//   int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
//   返回 0 成功 , 其它失败
int open_codec_result = avcodec_open2(avCodecContext, avCodec, 0);
//打开编解码器 失败处理
if(open_codec_result != 0){
    callHelper->onError(pid, 5);
    __android_log_print(ANDROID_LOG_ERROR , "FFMPEG" , "打开 编解码器 失败");
    return;
}




VI . FFMPEG 获取编解码器 代码示例


   

//视频 / 音频 处理需要的操作 ( 获取编解码器 )
        //① 查找 当前流 使用的编码方式 , 进而查找编解码器 ( 可能失败 , 不支持的解码方式 )
        AVCodec *avCodec = avcodec_find_decoder(codecParameters->codec_id);
        //查找失败处理
        if(avCodec == NULL){
            //如果没有找到编解码器 , 回调失败 , 方法直接返回 , 后续代码不执行
            callHelper->onError(pid, 2);
            __android_log_print(ANDROID_LOG_ERROR , "FFMPEG" , "查找 编解码器 失败");
            return;
        }
        //② 获取编解码器上下文
        AVCodecContext *avCodecContext = avcodec_alloc_context3(avCodec);
        //获取编解码器失败处理
        if(avCodecContext == NULL){
            callHelper->onError(pid, 3);
            __android_log_print(ANDROID_LOG_ERROR , "FFMPEG" , "创建编解码器上下文 失败");
            return;
        }
        //③ 设置 编解码器上下文 参数
        //      int avcodec_parameters_to_context(AVCodecContext *codec,
        //              const AVCodecParameters *par);
        //      返回值 > 0 成功 , < 0 失败
        int parameters_to_context_result =
                avcodec_parameters_to_context(avCodecContext, codecParameters);
        //设置 编解码器上下文 参数 失败处理
        if(parameters_to_context_result < 0){
            callHelper->onError(pid, 4);
            __android_log_print(ANDROID_LOG_ERROR , "FFMPEG" , "设置编解码器上下文参数 失败");
            return;
        }
        //④ 打开编解码器
        //   int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
        //   返回 0 成功 , 其它失败
        int open_codec_result = avcodec_open2(avCodecContext, avCodec, 0);
        //打开编解码器 失败处理
        if(open_codec_result != 0){
            callHelper->onError(pid, 5);
            __android_log_print(ANDROID_LOG_ERROR , "FFMPEG" , "打开 编解码器 失败");
            return;
        }


目录
相关文章
|
3月前
|
Linux 开发工具 Android开发
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
ijkplayer是由Bilibili基于FFmpeg3.4研发并开源的播放器,适用于Android和iOS,支持本地视频及网络流媒体播放。本文详细介绍如何在新版Android Studio中导入并使用ijkplayer库,包括Gradle版本及配置更新、导入编译好的so文件以及添加直播链接播放代码等步骤,帮助开发者顺利进行App调试与开发。更多FFmpeg开发知识可参考《FFmpeg开发实战:从零基础到短视频上线》。
304 2
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
|
3月前
|
编解码 语音技术 内存技术
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
《FFmpeg开发实战:从零基础到短视频上线》一书中的“5.1.2 把音频流保存为PCM文件”章节介绍了将媒体文件中的音频流转换为原始PCM音频的方法。示例代码直接保存解码后的PCM数据,保留了原始音频的采样频率、声道数量和采样位数。但在实际应用中,有时需要特定规格的PCM音频。例如,某些语音识别引擎仅接受16位PCM数据,而标准MP3音频通常采用32位采样,因此需将32位MP3音频转换为16位PCM音频。
110 0
FFmpeg开发笔记(五十八)把32位采样的MP3转换为16位的PCM音频
|
3月前
|
XML 开发工具 Android开发
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
ExoPlayer最初是为了解决Android早期MediaPlayer控件对网络视频兼容性差的问题而推出的。现在,Android官方已将其升级并纳入Jetpack的Media3库,使其成为音视频操作的统一引擎。新版ExoPlayer支持多种协议,解决了设备和系统碎片化问题,可在整个Android生态中一致运行。通过修改`build.gradle`文件、布局文件及Activity代码,并添加必要的权限,即可集成并使用ExoPlayer进行网络视频播放。具体步骤包括引入依赖库、配置播放界面、编写播放逻辑以及添加互联网访问权限。
229 1
FFmpeg开发笔记(五十六)使用Media3的Exoplayer播放网络视频
|
3月前
|
Web App开发 安全 程序员
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
多年的互联网寒冬在今年尤为凛冽,坚守安卓开发愈发不易。面对是否转行或学习新技术的迷茫,安卓程序员可从三个方向进阶:1)钻研谷歌新技术,如Kotlin、Flutter、Jetpack等;2)拓展新功能应用,掌握Socket、OpenGL、WebRTC等专业领域技能;3)结合其他行业,如汽车、游戏、安全等,拓宽职业道路。这三个方向各有学习难度和保饭碗指数,助你在安卓开发领域持续成长。
98 1
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
|
3月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
123 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
3月前
|
Android开发 开发者
FFmpeg开发笔记(五十七)使用Media3的Transformer加工视频文件
谷歌推出的Transformer,作为Jetpack Media3架构的一部分,助力开发者实现音视频格式转换与编辑。Media3简化了媒体处理流程,提升了定制性和可靠性。Transformer可用于剪辑、添加滤镜等操作,其示例代码可在指定GitHub仓库中找到。要使用Transformer,需在`build.gradle`中添加相关依赖,并按文档编写处理逻辑,最终完成音视频转换任务。具体步骤包括配置剪辑参数、设置空间效果以及监听转换事件等。
69 0
FFmpeg开发笔记(五十七)使用Media3的Transformer加工视频文件
|
3月前
|
Linux 视频直播
FFmpeg开发笔记(五十四)使用EasyPusher实现移动端的RTSP直播
本文介绍了如何使用EasyPusher-Android实现RTSP直播流程。首先对比了RTSP、RTMP、SRT和RIST四种流媒体协议,并以RTSP为例,详细说明了使用EasyPusher-Android向流媒体服务器进行RTSP直播推流的方法。文中还提供了OBS Studio配置RTSP插件及ZLMediaKit云服务器部署的相关信息,通过修改EasyPusher-Android源码使其支持通用RTSP地址,最终验证了直播功能的成功实现。
89 0
FFmpeg开发笔记(五十四)使用EasyPusher实现移动端的RTSP直播
|
存储 编解码 Java
【Android FFMPEG 开发】FFMPEG 音频重采样 ( 初始化音频重采样上下文 SwrContext | 计算音频延迟 | 计算输出样本个数 | 音频重采样 swr_convert )(一)
【Android FFMPEG 开发】FFMPEG 音频重采样 ( 初始化音频重采样上下文 SwrContext | 计算音频延迟 | 计算输出样本个数 | 音频重采样 swr_convert )(一)
764 0
|
Android开发
【Android FFMPEG 开发】FFMPEG 音频重采样 ( 初始化音频重采样上下文 SwrContext | 计算音频延迟 | 计算输出样本个数 | 音频重采样 swr_convert )(二)
【Android FFMPEG 开发】FFMPEG 音频重采样 ( 初始化音频重采样上下文 SwrContext | 计算音频延迟 | 计算输出样本个数 | 音频重采样 swr_convert )(二)
681 0
|
1月前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
57 19