分别从封装格式为mp4、flv、ts文件中提取出AVC(h264)并写入文件
/*****************************************************************//**
* \file WriteAVC.cpp
* \brief 提取视频中的视频流,写入为AVC(h264)文件
*
* \author 13648
* \date April 2024
*********************************************************************/
#define _CRT_SECURE_NO_WARNINGS
#include "myLog.h"
#include <iostream>
extern "C"
{
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavcodec/bsf.h>
}
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avcodec.lib")
/**
* 由于h264有两种格式,一种mp4格式(不带startcode,sps,pps信息)一种annexb格式(带startcode,sps、pps信息)
* 但是大多数的编解码器只支持annexb格式pkt数据,
* 因此在处理mp4或者flv格式的文件时需要使用h264_mp4toannexb比特流过滤器处理
*/
int main()
{
AVFormatContext* ifmt_ctx = NULL;
const std::string in_filename = "./MediaFile/input_5s.flv"; // input_5s.flv input_5s.ts
int nRet = avformat_open_input(&ifmt_ctx, in_filename.c_str(), NULL, NULL);
if (nRet != 0)
{
LOG_WARNING("avformat_open_input error\n");
return -1;
}
nRet = avformat_find_stream_info(ifmt_ctx, NULL);
if (nRet < 0)
{
LOG_WARNING("avformat_find_stream_info faild\n");
avformat_close_input(&ifmt_ctx);
return -2;
}
int video_idx = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (video_idx < 0)
{
LOG_WARNING("avformat_find_stream_info faild\n");
avformat_close_input(&ifmt_ctx);
return -3;
}
const std::string out_filename = "./MediaFile/input_5s.h264";
FILE* out_fp = fopen(out_filename.c_str(), "wb");
if (!out_fp)
{
LOG_WARNING("open out_filename faild\n");
avformat_close_input(&ifmt_ctx);
return -4;
}
// 获取比特流过滤器
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, ifmt_ctx->streams[video_idx]->codecpar);
av_bsf_init(bsf_ctx);
AVPacket* pkt = av_packet_alloc();
pkt->data = NULL;
pkt->size = 0;
while (true)
{
nRet = av_read_frame(ifmt_ctx, pkt);
if (nRet < 0)
{
LOG_WARNING("av_read_frame file finish\n");
break;
}
if (nRet == 0 && pkt->stream_index == video_idx)
{
#if 1
if (av_bsf_send_packet(bsf_ctx, pkt) != 0) // 发送包到比特流过滤器添加startcode、sps、pps信息
{
av_packet_unref(pkt);
continue;
}
av_packet_unref(pkt); // 清除,下面使用这个pkt接收bsf处理后的包
while (av_bsf_receive_packet(bsf_ctx, pkt) == 0) // 添加sps pps后可能不止一个包
{
size_t writeLen = fwrite(pkt->data, 1, pkt->size, out_fp);
if (writeLen != pkt->size)
{
LOG_WARNING("write pkt error\n");
}
fflush(out_fp);
av_packet_unref(pkt);
}
#else
// 如果是ts流(自带sps pps信息,可以直接写入)
size_t writeLen = fwrite(pkt->data, 1, pkt->size, out_fp);
if (writeLen != pkt->size)
{
LOG_WARNING("fwrite pkt error\n");
}
av_packet_unref(pkt);
#endif
}
else if(nRet == 0)
{
av_packet_unref(pkt);
}
}
if (pkt)
{
av_packet_free(&pkt);
}
if (out_fp)
{
fclose(out_fp);
}
if (bsf_ctx)
{
av_bsf_free(&bsf_ctx);
}
if (ifmt_ctx)
{
avformat_close_input(&ifmt_ctx);
}
return 0;
}