ffmpeg-从mp4、flv、ts文件中提取264视频流数据
main.c
#include <stdio.h>
#include <libavutil/log.h>
#include <libavformat/avio.h>
#include <libavformat/avformat.h>
void proc(int need_to_annexb, char* in_file, char* out_file)
{
AVFormatContext* ifmat_ctx = NULL;
int videoindex = -1;
AVPacket* pkt = NULL;
int ret = -1;
int file_end = 0;
//char* in_file = "believe.mp4";
//char* out_file = "out_mp4_no_annexb.h264";
FILE* out_fd = fopen(out_file, "wb");
printf("in_file = %s , out_file = %s\n", in_file, out_file);
//创建解复用器,最后使用avformat_close_input()释放相关内存
ifmat_ctx = avformat_alloc_context();
if(!ifmat_ctx)
{
printf("avformat_alloc_context faild!\n");
return -1;
}
//根据url打开码流,会选择匹配的解复用器的
ret = avformat_open_input(&ifmat_ctx, in_file, NULL, NULL);
if(ret != 0)
{
printf("avformat_open_input failed!\n");
return -1;
}
//读取媒体文件的部分数据包可以获取码流信息
ret = avformat_find_stream_info(ifmat_ctx, NULL);
if(ret < 0)
{
printf("avformat_find_stream_info faile!\n");
avformat_close_input(&ifmat_ctx);
return -1;
}
//查找出哪个码流是音频还是视频还是字幕
videoindex = av_find_best_stream(ifmat_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if(videoindex == -1)
{
printf("av_find_best_stream failed!\n");
avformat_close_input(&ifmat_ctx);
return -1;
}
//分配packet
pkt = av_packet_alloc();
av_init_packet(pkt);
file_end = 0;
while (0 == file_end)
{
if((ret = av_read_frame(ifmat_ctx, pkt)) < 0)
{
file_end = 1;
printf("av_read_frame end!\n");
}
//读出的帧判断是否是视频帧
if(ret == 0 && pkt->stream_index == videoindex)
{
//是否需要使用h264_mp4toannexb转换
if(need_to_annexb)
{
//获取比特流过滤器(h264_mp4toannexb)
const AVBitStreamFilter* bsfilter = av_bsf_get_by_name("h264_mp4toannexb");
AVBSFContext *bsf_ctx = NULL;
//申请过滤器上下文
av_bsf_alloc(bsfilter, &bsf_ctx);
//从视频流中拷贝编解决码器参数
avcodec_parameters_copy(bsf_ctx->par_in, ifmat_ctx->streams[videoindex]->codecpar);
//初始化过滤器上下文
av_bsf_init(bsf_ctx);
int input_size = pkt->size;
//记录下是否send 一个packet,receive 一个packet。基本都是这个情况的
//有比较少情况出现会send 一个packet,receive 几个packet
//(SPS、PPS、I帧在一个packet send,receive 多个packet)。
int out_pkt_count = 0;
if(av_bsf_send_packet(bsf_ctx, pkt) != 0)
{
//不管是否成功,都要释放packet,因为bitstreamfilter内部还有引用这个内存空间的
av_packet_unref(pkt);
continue;
}
//不管是否成功,都要释放packet,因为bitstreamfilter内部还有引用这个内存空间的
av_packet_unref(pkt);
while (av_bsf_receive_packet(bsf_ctx , pkt) == 0)
{
out_pkt_count++;
size_t size = fwrite(pkt->data, 1, pkt->size, out_fd);
if(size != pkt->size)
{
printf("fwrite failed!\n");
}
av_packet_unref(pkt);
}
//send 一个packet ,receive pakcet 超过2个就输出提示信息
if(out_pkt_count >= 2)
{
printf("one send packet size = %d, receive %d packet.\n", input_size,
out_pkt_count);
}
if(bsf_ctx)
av_bsf_free(&bsf_ctx);
}
else
{
size_t size = fwrite(pkt->data, 1, pkt->size, out_fd);
if(size != pkt->size)
{
printf("fwrite failed!\n");
}
av_packet_unref(pkt);
}
}
else
{
if(ret == 0)
{
av_packet_unref(pkt);
}
}
}
if(out_fd)
fclose(out_fd);
if(pkt)
av_packet_free(&pkt);
if(ifmat_ctx)
avformat_close_input(&ifmat_ctx);
}
int main()
{
proc(1, "believe.flv", "out_flv_need_toannexb.h264");//使用ffplay可以播放
proc(0, "believe.flv", "out_flv_no_toannexb.h264");//使用ffplay不可以
proc(1, "believe.mp4", "out_mp4_need_toannexb.h264");//使用ffplay可以播放
proc(0, "believe.mp4", "out_mp4_no_toannexb.h264");//使用ffplay不可以
proc(1, "believe.ts", "out_ts_need_toannexb.h264");//使用ffplay可以播放
proc(0, "believe.ts", "out_ts_no_toannexb.h264");//使用ffplay可以播放
//注意:
//flv/mp4/mkv一些结构中,h264需要h264_mp4toannexb处理,添加startcode/SPS/PPS等信息
//ts不用h264_mp4toannexb处理。
return 0;
}
flv两个文件的对比:
mp4两个文件的对比:
ts两个文件的对比: