0. 引言

最近一直在使用和学习ffmpeg. 工作中需要拉流解码, 获取音频和视频数据. 这些都是使用ffmpeg处理.

  因为对ffmpeg接触不多, 用的不深, 在使用的过程中经常遇到不太懂的地方, 就会花费很多时间去查阅资料. 所以自己对用到的知识点总结一下, 方便自己以后再重复用到时能够方便找到.

  环境: ubuntu16.04, 已安装ffmpeg依赖库. gcc编译工具.

ffmpeg解码过程中用到了两个很重要的结构体, 这两个结构体比较复杂, 用到的次数也非常多, 以后我单独写一篇进行总结.

    • AVPacket 保存未解码的数据.
    • AVFrame 保存解码后的数据.

1. 解码流程

2. 代码

 //***************************************************************
// @file: test.c
// @author: dingfang
// @date 2019-07-24 18:55:16
//*************************************************************** #include <stdio.h> #ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#ifdef __cplusplus
};
#endif int openCodecContext(const AVFormatContext *pFormatCtx, int *pStreamIndex, enum AVMediaType type, AVCodecContext **ppCodecCtx)
{
int streamIdx = -;
// 获取流下标
for (int i = ; i < pFormatCtx->nb_streams; i++)
{
if (pFormatCtx->streams[i]->codec->codec_type == type)
{
streamIdx = i;
break;
}
}
if (streamIdx == -)
{
printf("find video stream failed!\n");
exit(-);
}
// 寻找解码器
AVCodecContext *pCodecCtx = pFormatCtx->streams[streamIdx]->codec;
AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
if (NULL == pCodec)
{
printf("avcode find decoder failed!\n");
exit(-);
} //打开解码器
if (avcodec_open2(pCodecCtx, pCodec, NULL) < )
{
printf("avcode open failed!\n");
exit(-);
}
*ppCodecCtx = pCodecCtx;
*pStreamIndex = streamIdx; return ;
} int main(void)
{
AVFormatContext *pInFormatCtx = NULL;
AVCodecContext *pVideoCodecCtx = NULL;
AVCodecContext *pAudioCodecCtx = NULL;
AVPacket *pPacket = NULL;
AVFrame *pFrame = NULL;
int ret;
/* 支持本地文件和网络url */
const char streamUrl[] = "./test.flv"; /* 1. 注册 */
av_register_all(); pInFormatCtx = avformat_alloc_context(); /* 2. 打开流 */
if(avformat_open_input(&pInFormatCtx, streamUrl, NULL, NULL) != )
{
printf("Couldn't open input stream.\n");
return -;
} /* 3. 获取流的信息 */
if(avformat_find_stream_info(pInFormatCtx, NULL) < )
{
printf("Couldn't find stream information.\n");
return -;
} int videoStreamIdx = -;
int audioStreamIdx = -;
/* 4. 寻找并打开解码器 */
openCodecContext(pInFormatCtx, &videoStreamIdx, AVMEDIA_TYPE_VIDEO, &pVideoCodecCtx);
openCodecContext(pInFormatCtx, &audioStreamIdx, AVMEDIA_TYPE_AUDIO, &pAudioCodecCtx); pPacket = av_packet_alloc();
pFrame = av_frame_alloc(); int cnt = ;
while (cnt--)
{
/* 5. 读流数据, 未解码的数据存放于pPacket */
ret = av_read_frame(pInFormatCtx, pPacket);
if (ret < )
{
printf("av_read_frame error\n");
break;
} /* 6. 解码, 解码后的数据存放于pFrame */
/* 视频解码 */
if (pPacket->stream_index == videoStreamIdx)
{
avcodec_decode_video2(pVideoCodecCtx, pFrame, &ret, pPacket);
if (ret == )
{
printf("video decodec error!\n");
continue;
}
printf("* * * * * * video * * * * * * * * *\n");
printf("___height: [%d]\n", pFrame->height);
printf("____width: [%d]\n", pFrame->width);
printf("pict_type: [%d]\n", pFrame->pict_type);
printf("___format: [%d]\n", pFrame->format);
printf("* * * * * * * * * * * * * * * * * * *\n\n");
} /* 音频解码 */
if (pPacket->stream_index == audioStreamIdx)
{
avcodec_decode_audio4(pAudioCodecCtx, pFrame, &ret, pPacket);
if (ret < )
{
printf("audio decodec error!\n");
continue;
}
printf("* * * * * * audio * * * * * * * * * *\n");
printf("____nb_samples: [%d]\n", pFrame->nb_samples);
printf("__samples_rate: [%d]\n", pFrame->sample_rate);
printf("channel_layout: [%lu]\n", pFrame->channel_layout);
printf("________format: [%d]\n", pFrame->format);
printf("* * * * * * * * * * * * * * * * * * *\n\n");
}
av_packet_unref(pPacket);
} av_frame_free(&pFrame);
av_packet_free(&pPacket);
avcodec_close(pVideoCodecCtx);
avcodec_close(pAudioCodecCtx);
avformat_close_input(&pInFormatCtx); return ;
}

该代码不能直接编译, 编译需要依赖ffmpeg库. 包含ffmpeg动态库和makefile文件的压缩包地址: 点我下载

解压后, 进入目录, 使用make命令即可编译.

3. 函数说明

这里就几个比较重要的函数简单介绍一下.

av_register_all()        /* 使用ffmpeg几乎都要调用这一个函数, 注册ffmpeg各种编解码器, 复用器等. */

avformat_open_input()      /* 该函数用于打开本地多媒体文件或者网络流媒体url */

avformat_find_stream_info()   /* 该函数用于读取一部分音视频数据并且获得一些相关的信息 */

avcodec_find_decoder()    /* 由codec_id或者解码器名称来寻找对应的解码器 */

avcodec_open2()        /* 初始化解码器 */

av_read_frame()        /* 读流数据, 读出来的是压缩数据, 存放于AVPacket */

avcodec_decode_video2()    /* 视频解码 解码后数据为原始数据, 存放于AVFrame */

avcodec_decode_audio4()    /* 音频解码 解码后数据为原始数据, 存放于AVFrame */

ffmpeg解码音视频过程(附代码)的更多相关文章

  1. javaCV入门指南:调用FFmpeg原生API和JavaCV是如何封装了FFmpeg的音视频操作?

    通过"javaCV入门指南:序章 "大家知道了处理音视频流媒体的前置基本知识,基本知识包含了像素格式.编解码格式.封装格式.网络协议以及一些音视频专业名词,专业名词不会赘述,自行搜 ...

  2. FFmpeg处理音视频流程学习笔记

    原文作者:一叶知秋0830 链接:https://www.jianshu.com/p/1b715966af50 FFmpeg处理音视频完整流程包括5个阶段(输入文件—>编码数据包—>解码后 ...

  3. FFmpeg开发实战(五):FFmpeg 抽取音视频的视频数据

    如何使用FFmpeg抽取音视频的视频数据,代码如下: // FFmpegTest.cpp : 此文件包含 "main" 函数.程序执行将在此处开始并结束. // #include ...

  4. FFmpeg开发实战(四):FFmpeg 抽取音视频的音频数据

    如何使用FFmpeg抽取音视频的音频数据,代码如下: void adts_header(char *szAdtsHeader, int dataLen); // 使用FFmpeg从视频中抽取音频 vo ...

  5. iPhone调用ffmpeg2.0.2解码h264视频的示例代码

    iPhone调用ffmpeg2.0.2解码h264视频的示例代码 h264demo.zip 关于怎么在MAC下编译iOS下的ffmpeg请看 编译最新ffmpeg2.0.1(ffmpeg2.0.2)到 ...

  6. C#进程调用FFmpeg操作音视频

    项目背景 因为公司需要对音视频做一些操作,比如说对系统用户的发音和背景视频进行合成,以及对多个音视频之间进行合成,还有就是在指定的源背景音频中按照对应的规则在视频的多少秒钟内插入一段客户发音等一些复杂 ...

  7. FFmpeg Android 学习(一):Android 如何调用 FFMPEG 编辑音视频

    一.概述 在Android开发中,我们对一些音视频的处理比较无力,特别是编辑音视频这部分.而且在Android上对视频编辑方面,几乎没有任何API做支持,MediaCodec(硬编码)也没有做支持.那 ...

  8. ffmpeg 如何音视频同步

    转自:http://blog.csdn.net/yangzhiloveyou/article/details/8832516 output_example.c 中AV同步的代码如下(我的代码有些修改) ...

  9. 使用X264编码yuv格式的视频帧使用ffmpeg解码h264视频帧

    前面一篇博客介绍在centos上搭建点击打开链接ffmpeg及x264开发环境.以下就来问个样例: 1.利用x264库将YUV格式视频文件编码为h264格式视频文件 2.利用ffmpeh库将h264格 ...

随机推荐

  1. 顺序表应用7:最大子段和之分治递归法(SDUT 3664)

    #include <bits/stdc++.h> using namespace std; const int maxn = 50005; int num = 0; struct node ...

  2. cc.Sprite组件

    1.精灵 精灵(Sprite)是Cocos系列的核心概念之一,是Cocos Creator最常用的显示图像的组件. 游戏中显示一个图片,我们就可以把这个叫做"精灵" sprite, ...

  3. Git入门(待更)

    github是什么? 以下截取自百度百科 github: GitHub 是一个面向开源及私有软件项目的托管平台,因为只支持 Git 作为唯一的版本库格式进行托管,故名 GitHub. GitHub 于 ...

  4. 多层树级关系的json,递归删除空值的数据

    data =[{ "name": "省", "children":[ { "name": "市区", ...

  5. 有效管理进程的几个linux命令

    一般来说,应用程序进程的生命周期有三种主要状态:启动.运行和停止.如果我们想成为称职的管理员,每个状态都可以而且应该得到认真的管理.这八个命令可用于管理进程的整个生命周期. 启动进程 启动进程的最简单 ...

  6. 区间dp括号匹配

    POJ2955 匹配则加一,不需要初始化 //#include<bits/stdc++.h> #include<iostream> #include<cstdio> ...

  7. P3015 [USACO11FEB]最好的括号Best Parenthesis

    P3015 [USACO11FEB]最好的括号Best Parenthesis 题解 一定要开 long long !!! 通过阅读英文题面我们知道所给出的字符串是已经匹配好的,所以我们只是计算就好了 ...

  8. ubuntu 上开发.netcore

    ubuntu需要安装的软件: 1.sudo apt-get install openssh-server openssh-client 2.sudo apt-get git 3.安装vscode 4. ...

  9. StateListDrawable

    可供设置的属性如下: drawable:引用的Drawable位图,我们可以把他放到最前面,就表示组件的正常状态~ state_focused:是否获得焦点 state_window_focused: ...

  10. Qt编写自定义控件35-GIF录屏控件

    一.前言 在平时的写作过程中,经常需要将一些操作动作和效果图截图成gif格式,使得涵盖的信息更全面更生动,有时候可以将整个操作过程和运行效果录制成MP4,但是文件体积比较大,而且很多网站不便于上传,基 ...