首先,向雷霄骅,雷神致敬
上篇文章,即day04笔记:https://www.jianshu.com/p/f59bde2a848d留了一些作业,就是输出h264文件和yuv文件,不知道小伙伴们有没有弄出来,今天我这就把结果贴出来吧
首先,大家再来看下代码
#define __STDC_CONSTANT_MACROS
#include "stdafx.h"
#include "stdio.h"
//引入ffmpeg中的相关头文件
extern "C" {
#include "libavcodec\avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
}
int main(int argc, char* argv[])
{
//声明了AVFormatContext视频数据指针
AVFormatContext *pFormatCtx;
//声明变量i和视频的位置索引
int i, videoindex;
//解码器信息指针
AVCodecContext *pCodecCtx;
//解码器指针
AVCodec *pCodec;
//像素数据指针
AVFrame *pFrame, *pFrameYUV;
//可以理解成缓冲数组
uint8_t *out_buffer;
//h.264数据指针
AVPacket *packet;
int y_size;
int ret, got_picture;
struct SwsContext *img_convert_ctx;
//输入文件路径
char filepath[] = "Titanic.ts";
int frame_cnt;
//注册所有相关组件
av_register_all();
//初始化网络
avformat_network_init();
//分配空间
pFormatCtx = avformat_alloc_context();
//打开视频流
//如果没有找到avformat_open_input()方法如果返回0表示获取成功,反之则失败
if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0) {
printf("Couldn't open input stream.\n");
return -1;
}
//获取视频流信息 如果avformat_find_stream_info()结果大于等于0表示获取流成功
if (avformat_find_stream_info(pFormatCtx, NULL)<0) {
printf("Couldn't find stream information.\n");
return -1;
}
//ok 打开了视频并且获取了视频流 ,设置视频索引值默认值
videoindex = -1;
//循环遍历输入视频的av_Stream个数,依次判断av_Stream中的codec_type类型,如果是视频类型,进行记录,一般来说,这个值一般为0
for (i = 0; inb_streams; i++)
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
videoindex = i;
break;
}
//如果没有找到视频的索引,说明并不是一个视频文件
if (videoindex == -1) {
printf("Didn't find a video stream.\n");
return -1;
}
//取得视频的解码器信息
pCodecCtx = pFormatCtx->streams[videoindex]->codec;
//得到的解码器
pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
//解码器未找到
if (pCodec == NULL) {
printf("Codec not found.\n");
return -1;
}
//解码器和解码器信息的话,开始解码,如果返回值为0,说明无法开始解码
if (avcodec_open2(pCodecCtx, pCodec, NULL)<0) {
printf("Could not open codec.\n");
return -1;
}
/*
* 在此处添加输出视频信息的代码
* 取自于pFormatCtx,使用fprintf()
*/
/*printf("shipinshichang=%d\n",pFormatCtx->duration);
printf("shipingeshi=%s",pFormatCtx->iformat->name);*/
//定义文件fp
FILE *fp;
fopen_s(&fp,"file_info.txt", "wb+");
//往fp中输入文字
fprintf(fp,"shipinshichang=%d\n",pFormatCtx->duration);
fprintf(fp,"shipingeshi=%s",pFormatCtx->iformat->long_name);
//关闭文件
fclose(fp);
//初始化yuv容器,并且初始化内存空间
pFrame = av_frame_alloc();
pFrameYUV = av_frame_alloc();
out_buffer = (uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
//设置图像内容
avpicture_fill((AVPicture *)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
//初始化h.264容器
packet = (AVPacket *)av_malloc(sizeof(AVPacket));
//Output Info-----------------------------
printf("--------------- File Information ----------------\n");
av_dump_format(pFormatCtx, 0, filepath, 0);
printf("-------------------------------------------------\n");
//上文说的对图形进行宽度上方的裁剪,以便于显示的更好
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P,SWS_BICUBIC,NULL,NULL,NULL);
frame_cnt = 0;
FILE *fp_264;
//_wfopen_s(&fp_264,_T("fp264.h264"),_T("wb+"));
fopen_s(&fp_264, "fp264.h264", "wb+");
FILE *fp_yuv;
fopen_s(&fp_yuv, "fpyuv.yuv", "wb+");
//如果读流成功
while (av_read_frame(pFormatCtx, packet) >= 0) {
if (packet->stream_index == videoindex) {
/*
* 在此处添加输出H264码流的代码
* 取自于packet,使用fwrite()
*/
fwrite(packet->data, 1, packet->size, fp_264);
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
if (ret < 0) {
printf("Decode Error.\n");
return -1;
}
if (got_picture) {
//将数据写入到file文件中去
//fwrite(packet->data, 1, packet->size, fp_264);
//上文说的对图形进行宽度上方的裁剪,以便于显示的更好
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的代码
* 取自于pFrameYUV,使用fwrite()
*/
fwrite(pFrameYUV->data[0],1,(pCodecCtx->width)*(pCodecCtx->height),fp_yuv);
fwrite(pFrameYUV->data[1], 1, (pCodecCtx->width)*(pCodecCtx->height)/4, fp_yuv);
fwrite(pFrameYUV->data[2], 1, (pCodecCtx->width)*(pCodecCtx->height)/4, fp_yuv);
//计数
frame_cnt++;
}
}
//释放packet
av_free_packet(packet);
}
fclose(fp_264);
fclose(fp_yuv);
//释放相关资源
sws_freeContext(img_convert_ctx);
av_frame_free(&pFrameYUV);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return 0;
}
ok,上方中包括了信息的输出,txt文件的输出,h264的输出,yuv的输出。
在编写的时候需要注意fopen_s的使用。
txt输出,已经在day04提到了,今天我们来看下h264的输出。
h264文件的输出
上方中的相关代码如下
FILE *fp_264;
//_wfopen_s(&fp_264,_T("fp264.h264"),_T("wb+"));
fopen_s(&fp_264, "fp264.h264", "wb+");
FILE *fp_yuv;
fopen_s(&fp_yuv, "fpyuv.yuv", "wb+");
//如果读流成功
while (av_read_frame(pFormatCtx, packet) >= 0) {
if (packet->stream_index == videoindex) {
/*
* 在此处添加输出H264码流的代码
* 取自于packet,使用fwrite()
*/
fwrite(packet->data, 1, packet->size, fp_264);
很简单,是不是,但是 需要注意的是,while循环完成之后,需要把文件关闭,避免内存的浪费。
运行程序,如果出现这样的情况,说明程序已经成功执行了
用elecard streameye tools工具查看下h264文件,这个文件可以百度安装即可
可以看到图上有一些红点,红点就是day04说道的关键点
把点去掉之后,是这样的
这样看起来就是正常的了。
输出yuv文件
相关代码如下(部分代码,上方中创建fp_yuv文件代码就不贴了)
fwrite(pFrameYUV->data[0],1,(pCodecCtx->width)*(pCodecCtx->height),fp_yuv);
fwrite(pFrameYUV->data[1], 1, (pCodecCtx->width)*(pCodecCtx->height)/4, fp_yuv);
fwrite(pFrameYUV->data[2], 1, (pCodecCtx->width)*(pCodecCtx->height)/4, fp_yuv);
yuv:y :亮度 uv控制色度
当然如果只输出y 的话,即输出pFrameYUV->data[0]就是y,
依次类推,
data[1]就是u
data[2]就是v
下面我们看下只输出y的结果
如图所示,在我们打开yuv player的时候,会自动提示设置frame size,即图像的宽高,这个宽高我们看文件简介,或者借助mediainfo 工具看一眼即可,下图是我用mediainfo工具看到
显示是640*272像素的
当然不光像素需要设定,yuv pixel format那一栏中也要选择y,因为我们写入的文件就是y呀,哈哈
上面两项设定完,那么就可以播放了,如图
y只有亮度,所以看到的视频是黑白的(如果你想把一个彩色的视频转成黑白色,不妨用此案例试一试哦)
下面,我们来吧yuv都写入
代码就是上方显示的三个fwrite,至于uv那个大小的位置除以4,是因为我们要使用yuv420格式去显示文件,而这个格式的文件色度占用的大小是亮度大小的1/4,故除以了4,在生成yuv文件之后,需要再次设置格式
如图
播放效果如下
这样,视频才是彩色的。
ok,day04的作业就是这些了,大家有时间练习一下吧~~
这两天有点忙,但是我会抽时间写文章的,谢谢大家支持哦
下方是我的微信公众号,关注获取更多学习资源