[3] ffmpeg基础知识以及使用示例
2016-10-01 10:41
465 查看
* 学习自雷神的博客和视频,图片来自他的课件,雷神博客:*
http://blog.csdn.net/leixiaohua1020/
日期:2016.10.1
作者:isshe
github:github.com/isshe
邮箱:i.sshe@outlook.com
avformat: 封装格式的处理(flv,avi,mov)
swscale: 视频像素数据格式转换(常用于解码后视频的裁剪)。
avutil: 工具库。
avfilter: 滤镜特效处理。
avdevice: 各种设备的输入输出。
postproc: 后加工。
swresample: 音频采样数据格式转换。
前面四个是最常用的
* avcodec_decode_open2()这个函数是解码函数, 最主要的一个函数。
* 图中解码流程是:获取一个pakcet, 然后调用解码函数,把AVPacket结构中的data转换为AVFrame结构的data。
* AVPacket结构存储一帧压缩的编码数据。
* AVFrame结构存储一帧解码后的像素数据(对音频则是采样数据)
* AVFrame结构的元素data是双重指针,YUV数据来说包含data[0],data[1],data[2]分别存Y、U、V数据,注意每帧中U、V数据是Y数据的四分之一大小(对420P来说)。『Y:亮度数据, U,V:色差数据,由于人的眼睛对亮度更敏感,故而YUV数据中存更多的Y而减少UV的数据。当只有Y数据的时候,显示为黑白』
* 解码出来的数据可能函数无效像素。需要用sws_scale()函数处理(大概也可自编写函数处理)
* 如图:
*
* ffmpeg的函数简介:待更新(不定期更新)
* AVFormatContext是一个统筹全局的结构, 包含一些视频文件名,视频时长,视频码率等封装格式信息。
* AVInputFormat包含一些具体的视频格式信息,每种视频格式对应一个这个结构。
* 一般来说视频文件有两个流:视频流和音频流。有几个流就有几个AVStream数据结构, 一般视频流的index==0(也有其他情况), AVStream在AVFormatContext中是一个双重指针。
* AVCodecContext包含像素等编解码信息(对于视频)。
* AVCodec每种视/音频对应一个该结构体(例如h264).
* ffmpeg相关数据结构:待更新(不定期更新)
函数的调用流程如前所述。
需要注意,什么地方是什么数据。裸流数据在哪里输出,YUV数据在哪里输出。
输出YUV数据要注意Y数据是UV的4倍,data有3个。
找视频流的时候,需要一个一个流遍历,虽然一般index==0的那个是,但是也有例外。
*
这里要用g++, 因为用到的库x265中,用gcc编不过。
这份代码有很多警告,暂未处理。
ubuntu16.04中需要包含这个库才能不出现“未定义的引用”。
博客:http://blog.csdn.net/leixiaohua1020/article/details/15811977/
视频:http://blog.csdn.net/leixiaohua1020/article/details/47068015
http://blog.csdn.net/leixiaohua1020/
日期:2016.10.1
作者:isshe
github:github.com/isshe
邮箱:i.sshe@outlook.com
ffmpeg的基础知识
ffmpeg的库
avdecoc: 编解码。avformat: 封装格式的处理(flv,avi,mov)
swscale: 视频像素数据格式转换(常用于解码后视频的裁剪)。
avutil: 工具库。
avfilter: 滤镜特效处理。
avdevice: 各种设备的输入输出。
postproc: 后加工。
swresample: 音频采样数据格式转换。
前面四个是最常用的
ffmpeg的执行流程
* avcodec_decode_open2()这个函数是解码函数, 最主要的一个函数。
* 图中解码流程是:获取一个pakcet, 然后调用解码函数,把AVPacket结构中的data转换为AVFrame结构的data。
* AVPacket结构存储一帧压缩的编码数据。
* AVFrame结构存储一帧解码后的像素数据(对音频则是采样数据)
* AVFrame结构的元素data是双重指针,YUV数据来说包含data[0],data[1],data[2]分别存Y、U、V数据,注意每帧中U、V数据是Y数据的四分之一大小(对420P来说)。『Y:亮度数据, U,V:色差数据,由于人的眼睛对亮度更敏感,故而YUV数据中存更多的Y而减少UV的数据。当只有Y数据的时候,显示为黑白』
* 解码出来的数据可能函数无效像素。需要用sws_scale()函数处理(大概也可自编写函数处理)
* 如图:
*
* ffmpeg的函数简介:待更新(不定期更新)
ffmpeg解码相关数据结构
* AVFormatContext是一个统筹全局的结构, 包含一些视频文件名,视频时长,视频码率等封装格式信息。
* AVInputFormat包含一些具体的视频格式信息,每种视频格式对应一个这个结构。
* 一般来说视频文件有两个流:视频流和音频流。有几个流就有几个AVStream数据结构, 一般视频流的index==0(也有其他情况), AVStream在AVFormatContext中是一个双重指针。
* AVCodecContext包含像素等编解码信息(对于视频)。
* AVCodec每种视/音频对应一个该结构体(例如h264).
* ffmpeg相关数据结构:待更新(不定期更新)
使用示例
代码:
代码中打印相关结构信息的函数,删减了。函数的调用流程如前所述。
需要注意,什么地方是什么数据。裸流数据在哪里输出,YUV数据在哪里输出。
输出YUV数据要注意Y数据是UV的4倍,data有3个。
找视频流的时候,需要一个一个流遍历,虽然一般index==0的那个是,但是也有例外。
*
#include <stdio.h> //#include <libavformat/avformat.h> #define __STDC_CONSTANT_MACROS extern "C" { #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include <libswscale/swscale.h> //裁剪 } //输入和输出的文件名,解码前的数据放H264文件中,解码后放YUV文件中 #define FILENAME "cuc_ieschool.flv" #define FILENAME_YUV "cuc_ieschool_512x288.yuv" #define FILENAME_H264 "cuc_ieschool_512x288.H264" #define FILENAME_INFO "cuc_ieschool.info" void print_AVFormatContext_info(AVFormatContext *pFormatCtx, FILE *file_stream); int main(int argc, char *argv[]) { AVFormatContext *pFormatCtx; //统筹结构,保存封装格式相关信息 AVCodecContext *pCodecCtx; //保存视/音频编解码相关信息 AVCodec *pCodec; //每种编解码器对应一个结构体 AVFrame *pFrame; //解码后的结构 AVFrame *pFrameYUV; AVPacket *pPacket; //解码前 struct SwsContext *img_convert_ctx; //头文件只有一行,但是实际上这个结构体十分复杂 int i = 0; int video_index = 0; //为了检查哪个流是视频流,保存流数组下标 int y_size = 0; // int ret = 0; // int got_picture = 0; // char filename[256] = FILENAME; // int frame_cnt; //帧数计算 uint8_t *out_buffer; //??? FILE *fp = NULL; FILE *YUVfp = NULL; if (argc == 2) { memcpy(filename, argv[1], strlen(argv[1])+1); } else if (argc > 2) { printf("Usage: ./*.out video_filename"); } av_register_all(); //注册所有组件 avformat_network_init(); //初始化网络,貌似暂时没用到,可以试试删除 pFormatCtx = avformat_alloc_context(); //分配内存 //打开文件, 注意第一个参数是指针的指针 if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0) { printf("Couldn't open input stream.\n"); return (-1); } //获取流信息 if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { printf("Couldn't find stream info\n"); return (-1); } //找出视频流, nb_streams表示视频中有几种流 video_index = -1; for (i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { video_index = i; break; } } if (-1 == video_index) { printf("Couldn't find a video stream\n"); return (-1); } //复制编解码的信息到编解码的结构AVCodecContext结构中, //一方面为了操作结构中数据方便(不需要每次都从AVFormatContext结构开始一个一个指向) //另一方面方便函数的调用 pCodecCtx = pFormatCtx->streams[video_index]->codec; //可以查看源码avformat.h中定义的结构 //找到一个和视频流对应的解码器 pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if (NULL == pCodec) { printf("Couldn't found a decoder\n"); return (-1); } //打开解码器 if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { printf("Couldn't open decoder\n"); return (-1); } //输出一些想要输出的信息 fp = stdout; fp = fopen(FILENAME_INFO, "w+"); if (fp == NULL) { fprintf(stderr, "fopen error\n"); return (-1); } //输出结构中的信息以更了解这些结构。这里代码只示例一个。 print_AVFormatContext_info(pFormatCtx, fp); //输出视频文件信息 printf("------------------------file infomation-----------------------------\n"); av_dump_format(pFormatCtx, 0, filename, 0); printf("--------------------------------------------------------------------\n"); pFrame = av_frame_alloc(); pFrameYUV = av_frame_alloc(); out_buffer = (uint8_t *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height)); avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height); //??? img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL); pPacket = (AVPacket *)av_malloc(sizeof(AVPacket)); frame_cnt = 0; //帧数计算吗? //打开文件 fp = fopen(FILENAME_H264, "w+"); if (fp == NULL) { fprintf(stderr, "h264 fopen error\n"); return (-1); } YUVfp = fopen(FILENAME_YUV, "w+"); if (NULL == YUVfp) { fprintf(stderr, "YUV fopen error\n"); return (-1); } while(av_read_frame(pFormatCtx, pPacket) >= 0) { if (pPacket->stream_index == video_index) { //这里可以输出H264马流信息,这里是解码前 fwrite(pPacket->data, pPacket->size, 1, fp); //解码 ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, pPacket); if (ret < 0) { printf("Decode fail\n"); return (-1); } if (got_picture == 0) { printf("get picture error\n"); } else { //调整解码出来的图像,解码出来的可能含有填充的信息。 sws_scale(img_convert_ctx, (const uint8_t * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize); // printf("Decoded frame index: %d\n", frame_cnt); // 已经解码,可以输出YUV的信息 // 这个data是一个数组,需要注意!一般3个:代表Y、U、V // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! fwrite(pFrameYUV->data[0], 1, pCodecCtx->width * pCodecCtx->height, YUVfp); fwrite(pFrameYUV->data[1], 1, pCodecCtx->width * pCodecCtx->height / 4, YUVfp); fwrite(pFrameYUV->data[2], 1, pCodecCtx->width * pCodecCtx->height / 4, YUVfp); frame_cnt++; } } av_free_packet(pPacket); } printf("Decoded frame count: %d\n", frame_cnt); fclose(fp); fclose(YUVfp); sws_freeContext(img_convert_ctx); av_frame_free(&pFrameYUV); av_frame_free(&pFrame); avcodec_close(pCodecCtx); avformat_close_input(&pFormatCtx); return 0; } void print_AVFormatContext_info(AVFormatContext *pFormatCtx, FILE *file_stream) { fprintf(file_stream, "-------------------------AVFormatContext--------------------------\n"); fprintf(file_stream, "ctx_flags = %d\n" "nb_streams = %u\n" "filename = %s\n" "start_time = %d\n" "duration = %d\n" "bit_rate = %d\n" "packet_size = %u\n" "max_delay = %d\n" "flags = %d\n" "probesize = %d\n" "key = %d\n" "keylen = %d\n" "nb_programs = %d\n", pFormatCtx->ctx_flags, pFormatCtx->nb_streams, pFormatCtx->filename, pFormatCtx->start_time, pFormatCtx->duration, pFormatCtx->bit_rate, pFormatCtx->packet_size, pFormatCtx->max_delay, pFormatCtx->flags, pFormatCtx->probesize, pFormatCtx->key, pFormatCtx->keylen, pFormatCtx->nb_programs); fprintf(file_stream, "------------------------------------------------------------------\n"); }
编译
这个要注意,ubuntu下弄了好久。这里要用g++, 因为用到的库x265中,用gcc编不过。
这份代码有很多警告,暂未处理。
g++ ffmpeg_decoder.c -o ffmpeg_decoder.out -O2 -Wall -g \ -lavformat -lavcodec -lavformat -lavutil -lswresample -lswscale \ -lx264 -lx265 -lvpx -lmp3lame -lopus -lfdk-aac -lX11 -lva -lvdpau -lva-drm \ -lva-x11 -lvorbisenc -lvorbis -ltheoraenc -ltheoradec -ldl -lm -lpthread -lz
ubuntu16.04中需要包含这个库才能不出现“未定义的引用”。
结果
相关结构的信息存到.info文件,裸流数据存到h264文件,解码后数据存yuv文件。
参考资料
雷神的视频,课件。博客:http://blog.csdn.net/leixiaohua1020/article/details/15811977/
视频:http://blog.csdn.net/leixiaohua1020/article/details/47068015
代码下载
http://download.csdn.net/detail/i_scream_/9644386相关文章推荐
- 用python处理文本,本地文件系统以及使用数据库的知识基础
- Filter的创建和使用以及基础知识
- Notification(一)——基础知识使用示例
- 数据库—基础知识以及框架FMDB的使用
- java-day05-Thread-基础知识以及运用API第一种方法使用Thread类
- css基础知识之选择器使用示例
- iOS基础知识:if 语句以及if语句的使用注意事项
- 【JAVA基础知识总结】Java I/0流概述以及使用方法
- Linux基础知识学习(二)----SZ/RZ命令讲解以及在secureCRT使用SFTP上传文件的讲解
- 在ASP.NET Atlas中调用Web Service——创建Mashup调用远端Web Service(基础知识以及简单示例)
- tomcat7日志管理--基础知识、配置、以及使用log4j做日志分割
- 在ASP.NET Atlas中调用Web Service——创建Mashup调用远端Web Service(基础知识以及简单示例)
- Java基础知识强化之集合框架笔记19:List集合迭代器使用之 并发修改异常的产生原因 以及 解决方案
- 硬盘基础知识以及在Linux系统中的使用
- 小东吖 之 java的基础知识以及break 和 continue 关键字的使用
- PHP基础知识之————PHP Web脚本中使用FFmpeg
- 硬盘基础知识以及在Linux系统中的使用
- 【JAVA基础知识总结】Java I/0流概述以及使用方法
- Notification(一)——基础知识使用示例