av_parse_parse2 用于从输入的数据流中将流中的压缩编码数据一帧一帧的分离出来,也就是从一端数据码流中将需要解码的packet读取出来
由于H264由特殊的格式组成 [StartCode] [NALU Header] [NALU Payload] ,其中 Start Code ⽤于标示这是⼀个NALU 单元的开 始,必须是"00 00 00 01" 或"00 00 01",除此之外基本相当于⼀个NAL header + RBSP;其中NALU Hearder,,长度一个字节,后面5位代表了这一帧的具体属性,比如IDR,psp,sps等

核心函数是av_parser_parse2():
av_parser_parse2():从输入的数据流中解析数据获得一个Packet, 从输入的数据流中分离出一帧一帧的压缩编码数据。
这个函数的功能是从一大片数据中,分割出一个个nal单元,这也是为什么后面使用fread读取文件流,使用av_parser_parse2就可以获取出packet

解码视频H264

//解码视频,使用了av_parser_parse2

/*** @file* video decoding with libavcodec API example** @example decode_video.c*/#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <libavcodec/avcodec.h>#define INBUF_SIZE 4096static void pgm_save(unsigned char *buf, int wrap, int xsize, int ysize,char *filename)
{FILE *f;int i;f = fopen(filename,"wb");fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255);for (i = 0; i < ysize; i++)fwrite(buf + i * wrap, 1, xsize, f);fclose(f);
}static void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt,const char *filename)
{char buf[1024];int ret;ret = avcodec_send_packet(dec_ctx, pkt);if (ret < 0) {fprintf(stderr, "Error sending a packet for decoding\n");exit(1);}while (ret >= 0) {ret = avcodec_receive_frame(dec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)return;else if (ret < 0) {fprintf(stderr, "Error during decoding\n");exit(1);}printf("saving frame %3d\n", dec_ctx->frame_number);fflush(stdout);/* the picture is allocated by the decoder. no need tofree it */snprintf(buf, sizeof(buf), "%s-%d", filename, dec_ctx->frame_number);pgm_save(frame->data[0], frame->linesize[0],frame->width, frame->height, buf);}
}//从本地读取一个h264/265文件,但是一定要保证这个文件没有被封装过
//输入必须是只包含视频编码数据“裸流”(例如H.264、HEVC码流文件),而不能是包含封装格式的媒体数据(例如AVI、MKV、MP4)
int main(int argc, char **argv)
{const char *filename, *outfilename;const AVCodec *codec;   // 编解码器AVCodecParserContext *parser; // 码流解析器AVCodecContext *c= NULL;  // 编解码器上下文FILE *f;AVFrame *frame;uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];uint8_t *data;size_t   data_size;int ret;AVPacket *pkt;if (argc <= 2) {fprintf(stderr, "Usage: %s <input file> <output file>\n""And check your input file is encoded by mpeg1video please.\n", argv[0]);exit(0);}filename    = argv[1];outfilename = argv[2];pkt = av_packet_alloc();if (!pkt)exit(1);/* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);/* find the MPEG-1 video decoder *///查找解码器//比如这里AVCodecContext和codec = avcodec_find_decoder(AV_CODEC_ID_H264);if (!codec) {fprintf(stderr, "Codec not found\n");exit(1);}//初始化解析器,也就是根据codec->id来确定解析器的具体类型,解析器具体类型可以看文章最后。parser = av_parser_init(codec->id);if (!parser) {fprintf(stderr, "parser not found\n");exit(1);}// 根据编解码器创建编解码器上下文,为什么创建上下文在之前的文章有介绍c = avcodec_alloc_context3(codec);if (!c) {fprintf(stderr, "Could not allocate video codec context\n");exit(1);}/* For some codecs, such as msmpeg4 and mpeg4, width and heightMUST be initialized there because this information is notavailable in the bitstream. *//* open it *///打开解码器//如果是cpu解码,可以设置解码线程数量//分配AVCodecContext/*** thread count* is used to decide how many independent tasks should be passed to execute()* - encoding: Set by user.* - decoding: Set by user.*/c->thread_count=10;//打开解码器,并将解码器和上下文建立连接if (avcodec_open2(c, codec, NULL) < 0) {fprintf(stderr, "Could not open codec\n");exit(1);}f = fopen(filename, "rb");if (!f) {fprintf(stderr, "Could not open %s\n", filename);exit(1);}frame = av_frame_alloc();if (!frame) {fprintf(stderr, "Could not allocate video frame\n");exit(1);}while (!feof(f)) {/* read raw data from the input file */data_size = fread(inbuf, 1, INBUF_SIZE, f);if (!data_size)break;/* use the parser to split the data into frames */data = inbuf;//注意这里,这里是个小循环,也就是说,data中有可能包含多个nal单元,//每次循环只会找出一个nal,循环,直到所有nal找出来为止while (data_size > 0) {//这里就是最核心的部分,从输入的码流中查找NALU,然后将读取的NALU数据放到pkt的data中,然后返回的pkt->size大于0,这说明成功分离除了一个NALUret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);if (ret < 0) {fprintf(stderr, "Error while parsing\n");exit(1);}//这里向前移位data      += ret;data_size -= ret;if (pkt->size)decode(c, frame, pkt, outfilename);}}/* flush the decoder */decode(c, frame, NULL, outfilename);fclose(f);av_parser_close(parser);avcodec_free_context(&c);av_frame_free(&frame);av_packet_free(&pkt);return 0;
}

解码音频AAC或MP3

/**
* @brief         解码音频,主要的测试格式aac和mp3
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <libavutil/frame.h>
#include <libavutil/mem.h>#include <libavcodec/avcodec.h>#define AUDIO_INBUF_SIZE 20480
#define AUDIO_REFILL_THRESH 4096static char err_buf[128] = {0};
static char* av_get_err(int errnum)
{av_strerror(errnum, err_buf, 128);return err_buf;
}static void print_sample_format(const AVFrame *frame)
{printf("ar-samplerate: %uHz\n", frame->sample_rate);  // 音频采样率printf("ac-channel: %u\n", frame->channels);          // 音频信道数目printf("f-format: %u\n", frame->format);// 格式需要注意,实际存储到本地文件时已经改成交错模式
}static void decode(AVCodecContext *dec_ctx, AVPacket *pkt, AVFrame *frame,FILE *outfile)
{int i, ch;int ret, data_size;/* send the packet with the compressed data to the decoder */ret = avcodec_send_packet(dec_ctx, pkt);   //向解码器发送packet,进行解码if(ret == AVERROR(EAGAIN)){fprintf(stderr, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");}else if (ret < 0){fprintf(stderr, "Error submitting the packet to the decoder, err:%s, pkt_size:%d\n",av_get_err(ret), pkt->size);
//        exit(1);return;}/* read all the output frames (infile general there may be any number of them */while (ret >= 0){// 对于frame, avcodec_receive_frame内部每次都先调用ret = avcodec_receive_frame(dec_ctx, frame);   // 从解码器读取出已经解码的frameif (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)return;else if (ret < 0){fprintf(stderr, "Error during decoding\n");exit(1);}data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);if (data_size < 0){/* This should not occur, checking just for paranoia */fprintf(stderr, "Failed to calculate data size\n");exit(1);}static int s_print_format = 0;if(s_print_format == 0){s_print_format = 1;print_sample_format(frame);}/**P表示Planar(平面),其数据格式排列方式为 :LLLLLLRRRRRRLLLLLLRRRRRRLLLLLLRRRRRRL...(每个LLLLLLRRRRRR为一个音频帧)而不带P的数据格式(即交错排列)排列方式为:LRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRLRL...(每个LR为一个音频样本)播放范例:   ffplay -ar 48000 -ac 2 -f f32le believe.pcm*/for (i = 0; i < frame->nb_samples; i++){for (ch = 0; ch < dec_ctx->channels; ch++)  // 交错的方式写入, 大部分float的格式输出fwrite(frame->data[ch] + data_size*i, 1, data_size, outfile);}}
}
// 播放范例:   ffplay -ar 48000 -ac 2 -f f32le believe.pcm//从main函数看起,这个案例是从本地读取一个h264/265,aac或mp3文件,(当然也可以是其它类型文件)
//但是一定要保证这个文件没有被封装过
//输入必须是只包含视频编码数据“裸流”(例如H.264、HEVC码流aac,MP3文件),而不能是包含封装格式的媒体数据(例如AVI、MKV、MP4)
int main(int argc, char **argv)
{const char *outfilename;const char *filename;const AVCodec *codec;AVCodecContext *codec_ctx= NULL;AVCodecParserContext *parser = NULL;int len = 0;int ret = 0;FILE *infile = NULL;FILE *outfile = NULL;uint8_t inbuf[AUDIO_INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];uint8_t *data = NULL;size_t   data_size = 0;AVPacket *pkt = NULL;AVFrame *decoded_frame = NULL;if (argc <= 2){fprintf(stderr, "Usage: %s <input file> <output file>\n", argv[0]);exit(0);}filename    = argv[1];outfilename = argv[2];pkt = av_packet_alloc();enum AVCodecID audio_codec_id = AV_CODEC_ID_AAC;if(strstr(filename, "aac") != NULL){audio_codec_id = AV_CODEC_ID_AAC;}else if(strstr(filename, "mp3") != NULL){audio_codec_id = AV_CODEC_ID_MP3;}else{printf("default codec id:%d\n", audio_codec_id);}//这里是寻找解码器,一般来说,ffmpeg都包含一个contex上下文,然后其中包含一个具体的业务指针codec = avcodec_find_decoder(audio_codec_id);  // AV_CODEC_ID_AAC或AV_CODEC_ID_MP3if (!codec) {fprintf(stderr, "Codec not found\n");exit(1);}// 获取裸流的解析器 AVCodecParserContext(数据)  +  AVCodecParser(方法)// 初始化解析器,也就是根据codec->id来确定解析器的具体类型parser = av_parser_init(codec->id);if (!parser) {fprintf(stderr, "Parser not found\n");exit(1);}// 分配解码器codec上下文: AVCodecContext* codec_ctxcodec_ctx = avcodec_alloc_context3(codec);if (!codec_ctx) {fprintf(stderr, "Could not allocate audio codec context\n");exit(1);}// 将解码器和解码器上下文进行关联,打开解码器if (avcodec_open2(codec_ctx, codec, NULL) < 0) {fprintf(stderr, "Could not open codec\n");exit(1);}// 打开输入文件infile = fopen(filename, "rb");if (!infile) {fprintf(stderr, "Could not open %s\n", filename);exit(1);}// 打开输出文件outfile = fopen(outfilename, "wb");if (!outfile) {av_free(codec_ctx);exit(1);}// 读取文件进行解码data      = inbuf;data_size = fread(inbuf, 1, AUDIO_INBUF_SIZE, infile);while (data_size > 0){if (!decoded_frame){if (!(decoded_frame = av_frame_alloc())){fprintf(stderr, "Could not allocate audio frame\n");exit(1);}}ret = av_parser_parse2(parser, codec_ctx, &pkt->data, &pkt->size,data, data_size,AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);if (ret < 0){fprintf(stderr, "Error while parsing\n");exit(1);}data      += ret;   // 跳过已经解析的数据data_size -= ret;   // 对应的缓存大小也做相应减小if (pkt->size)decode(codec_ctx, pkt, decoded_frame, outfile);if (data_size < AUDIO_REFILL_THRESH)    // 如果数据少了则再次读取{memmove(inbuf, data, data_size);    // 把之前剩的数据拷贝到buffer的起始位置data = inbuf;// 读取数据 长度: AUDIO_INBUF_SIZE - data_sizelen = fread(data + data_size, 1, AUDIO_INBUF_SIZE - data_size, infile);if (len > 0)data_size += len;}}/* 冲刷解码器 */pkt->data = NULL;   // 让其进入drain modepkt->size = 0;decode(codec_ctx, pkt, decoded_frame, outfile);fclose(outfile);fclose(infile);avcodec_free_context(&codec_ctx);av_parser_close(parser);av_frame_free(&decoded_frame);av_packet_free(&pkt);printf("main finish, please enter Enter and exit\n");return 0;
}

ffmpeg基础五:单独解码裸流aac或MP3或H264相关推荐

  1. ffmpeg 解码音频(aac、mp3)输出pcm文件

    ffmpeg 解码音频(aac.mp3)输出pcm文件 播放pcm可以参考: ffplay -ar 48000 -ac 2 -f f32le out.pcm main.c #include <s ...

  2. FFmpeg系列(二)—— 音视频裸流转换:mp3转pcm、h264转YUV

    文章目录 1.总流程 2.解析流程 3.解码流程 4.完整代码 1.总流程 创建解析器.解码器.AVPacket和AVFrame 打开文件,将mp3数据读入缓冲区 解析mp3数据(在 main 函数中 ...

  3. TS科普19 各种流(如:MP3、H264、H265等)在TS的流类型

    在PMT中会定义当前节目其音频流对应的PID以及视频流对应的PID 其中的字段stream_type 决定了当前的流类型 此为8比特字段,指示具有PID值的包内承载的节目元类型,其PID值由eleme ...

  4. FFmpeg基础简介

    背景介绍 FFmpeg 是一款音视频编解码工具,同时也是一组音视频编解码开发套件,为音视频开发者提供了丰富的音视频处理的调用接口. FFmpeg中的"FF"指的是"Fas ...

  5. ffmpeg 解码本地无封装裸音频流 AAC MP3 复制代码就可以运行

    ⾳频解码过程 FFmpeg对应流程 关键函数说明: avcodec_find_decoder:根据指定的AVCodecID查找注册的解码器. av_parser_init:初始化AVCodecPars ...

  6. FFmpeg解码H264裸流并转换成opencv Mat

    感谢雷霄骅博士的在中文视频编解码的付出,http://blog.csdn.net/leixiaohua1020 最近要搞一些视频推流的事情,要解析H264裸流并且获取opencv格式的Mat数据给算法 ...

  7. ffmpeg 找不到bin_FFmpeg开发笔记(九):ffmpeg解码rtsp流并使用SDL同步播放

    若该文为原创文章,转载请注明原文出处 本文章博客地址:https://blog.csdn.net/qq21497936/article/details/109603499 各位读者,知识无穷而人力有穷 ...

  8. FFMPEG视音频编解码零基础学习方法

    总结]FFMPEG视音频编解码零基础学习方法 在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频编解码的人,有的已经是有多年经验的"大神",有的是刚 ...

  9. 音视频开发(5)---FFMPEG视音频编解码零基础学习方法

    FFMPEG视音频编解码零基础学习方法 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/leixiaohua1020/article/details/ ...

  10. ffmpeg基础三:H264,从MP4文件获取(av_bsf_get_by_name(“h264_mp4toannexb“))和从TS流获取保存H264

    参考:零声学院 1.流媒体编解码流程 2.编码原理 在⾳视频传输过程中,视频⽂件的传输是⼀个极⼤的问题:⼀段分辨率为19201080,每个像 素点为RGB占⽤3个字节,帧率是25的视频,对于传输带宽的 ...

最新文章

  1. 从谷歌的招聘中,我们学到了什么?
  2. 科大星云诗社动态20210227
  3. 用python编制一个的类_常见面试题整理--Python概念篇
  4. java逸出_Java并发编程 - 对象的共享
  5. python 堆_面试再问你什么是堆和栈,你就把这篇文章甩给他
  6. LeetCode 680 验证回文字符串 Ⅱ
  7. 使用VisualStudio或VisualStudio Code作为代码比较工具
  8. androidstuio实现页面跳转_vue-router 基础:4类路由跳转示例
  9. sprintf函数的用法_我在C++项目中对于宏的一些用法
  10. Spark的三种运行模式
  11. 【计量经济学导论】10. ARIMA模型
  12. rabbitm rpm傻瓜式安装
  13. 回声消除(AEC)初体验之matlab仿真
  14. 我的上司叫“专横”,驾驭他小case,情商修炼术
  15. BT下载会损害硬盘吗?--硬盘的工作原理,硬盘寿命
  16. 想成为我的同事,不会点Linux怎么行!
  17. Excel导出 并完成后自动打开
  18. C语言基础 判断周几
  19. 正确率、召回率、F值例子
  20. git代码使用空格缩进

热门文章

  1. cogs 1752 [BOI2007]摩基亚Mokia(cdq分治+树状数组)
  2. 爬取微博视频页并批量下载python+requests+ffmpeg(连接视频和音频)
  3. Java岗大厂面试百日冲刺 - 日积月累,每日三题【Day39】—— 数据库6
  4. 电商订单价格计算逻辑
  5. 研究所北航计算机录取分数线,收藏!2020年北京航空航天大学录取分数线大汇总...
  6. 大疆精灵4rtk照片信息读取
  7. 元宵节没用智能名片在互联网发贺卡,那就OUT了
  8. 会话管理:Cookie和Session
  9. 跟着安全牛大表哥学渗透
  10. 支付宝提现APP服务端PHP