之前参照雷霄骅博士的最简单的基于FFMPEG的封装格式转换器(无编解码)的博客和FFmpeg官网的example,实现一个简单的封装格式转换器。但是后来我发现我想从mp4格式转换成avi格式的时候会报如下错误,如图:
H.264 bitstream malformed, no startcode found, use the video bitstream
filte
结构体:AVBSFContext;AVBitStreamFilter;
函数:av_bsf_get_by_name()//用于初始化AVBitStreamFilte;
av_bsf_alloc();//为AVBSFContext分配空间
av_bsf_init();//Prepare the filter for use, after all the parameters and options have been set.就是参数设置好以后在运行该函数。
av_bsf_free();//空间释放函数,该函数会释放掉其内部所有内存,在为
其结构体AVBSFContext设置参数后释放可能会导致程序中断,下面会讲到。
//初始化
AVBSFContext* v_ctx = NULL;
const AVBitStreamFilter* v_filter = av_bsf_get_by_name("h264_mp4toannexb");
if (!v_filter)
{
std::cout << "av_bsf_get_by_name failed! could not muxing the format of avi.etc" << std::endl;
}
ret=av_bsf_alloc(v_filter, &v_ctx);
//设置参数
if (in_codecpar->codec_type == AVMEDIA_TYPE_VIDEO&& need_filter==true)
{
avcodec_parameters_copy(p, in_codecpar);
v_ctx->par_in =p;
av_bsf_init(v_ctx);
}
在这里特殊强调一下,我们不需要使用avcodec_parameters_free(&p)
来释放p的内存,因为av_bsf_free会把与其关联的内存全部释放掉,如果使用了avcodec_parameters_free(&p)来释放内存会导致程序在最后中断掉。
/*参数释放
av_bsf_free(&v_ctx);
修改过后的版本为1.1版本,已经支持了各种格式之间相互转换,包括mkv,mov,mp4,flv,avi,wmv,ts,打印log会降低转换速度,如需提速请将一下代码注释掉:
if (frame_index%3000==0)
{
std::cout << "Write frames to output file:" << frame_index << std::endl;
}
/*
@USER wangyu
@DATE 2020-08-06
@VERSION 1.1
@[email protected]
*/
#include
extern "C"
{
#include
#include
}
int main(int argc,char *argv[])
{
//输出格式
AVOutputFormat* ofmt = NULL;
//输入于输出媒体句柄。
AVFormatContext* ifmt_ctx = NULL, * ofmt_ctx = NULL;
AVPacket* pkt = NULL;
//输入输出文件名字。
const char* in_filename, * out_filename;
/*in_filename = "test.mp4";
out_filename = "test1.mov";*/
int ret = -1;
//用于存储输入流的索引。
int* stream_mapping = NULL;
int stream_index = 0;
int frame_index = 0;
int videostream = -1;
////新的api av_bsf_get_by_name(), av_bsf_alloc(), and av_bsf_init()
bool need_filter = false;
AVCodecParameters* p = avcodec_parameters_alloc();
AVBSFContext* v_ctx = NULL;
const AVBitStreamFilter* v_filter = av_bsf_get_by_name("h264_mp4toannexb");
if (!v_filter)
{
std::cout << "av_bsf_get_by_name failed! could not muxing the format of avi.etc" << std::endl;
}
ret=av_bsf_alloc(v_filter, &v_ctx);
if (argc < 2)
{
std::cout << "请键入输入文件和输出文件参数重新执行该程序!" << std::endl;
return -1;
}
in_filename = argv[1];
//不想填写命令可直接在这里更改argv[2]为输出文件名
//支持mkv,mp4,ts,wmv,avi,mov,flv。
out_filename =argv[2];
//打开源文件。
ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0);
if (ret < 0)
{
std::cout << "could not open the filename :" << in_filename << std::endl;
goto end;
}
else {
std::cout << "open the filename :" << in_filename << "success" << std::endl;
}
ret = avformat_find_stream_info(ifmt_ctx, 0);
if (ret < 0) {
std::cout << "avformat_find_stream_info filed!" << std::endl;
goto end;
}
videostream = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
//打印详细信息。
av_dump_format(ifmt_ctx, 0, in_filename, 0);
stream_mapping = new int[ifmt_ctx->nb_streams];
//为输出的media handle申请空间
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
if (!ofmt_ctx) {
std::cout << "Could not create output context!" << std::endl;
avformat_close_input(&ifmt_ctx);
goto end;
}
ofmt = ofmt_ctx->oformat;
//判断输出视频格式后缀,来判断是否需要使用过滤器。
ret=strcmp((const char*)ofmt->name, "avi");
if (ret == 0)
{
need_filter = true;
}
for (int i = 0; i < ifmt_ctx->nb_streams; i++)
{
//根据输入流创建输出流(Create output AVStream according to input AVStream)
AVStream* in_stream = ifmt_ctx->streams[i];
AVCodecParameters* in_codecpar = in_stream->codecpar;
AVStream* out_stream = NULL;
if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
{
//这里将丢弃的流值存为-1,方便后续读帧时丢弃。
stream_mapping[i] = -1;
continue;
}
//将要保留的媒体流进行索引存储。
stream_mapping[i] = stream_index;
stream_index++;
//新建一个媒体流
out_stream = avformat_new_stream(ofmt_ctx, NULL);
if (!out_stream)
{
std::cout << "avformat_new_stream failed" << std::endl;
avformat_close_input(&ifmt_ctx);
goto end;
}
//参数拷贝
ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
if (ret < 0)
{
std::cout << "avcodec_parameters_copy filed!" << std::endl;
goto end;
}
if (in_codecpar->codec_type == AVMEDIA_TYPE_VIDEO&& need_filter==true)
{
avcodec_parameters_copy(p, in_codecpar);
v_ctx->par_in =p;
av_bsf_init(v_ctx);
}
out_stream->codecpar->codec_tag = 0;
}
//输出一下格式------------------
av_dump_format(ofmt_ctx, 0, out_filename, 1);
//打开输出文件(Open output file)
if (!(ofmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
if (ret < 0) {
std::cout << "could not open the outputfile:" << out_filename << std::endl;
goto end;
}
}
//写文件头(Write file header)
ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0) {
std::cout << "avformat_write_header failed!" << std::endl;
goto end;
}
pkt = av_packet_alloc();
while (1)
{
AVStream* in_stream=NULL;
AVStream* out_stream=NULL;
//获取pkt
ret = av_read_frame(ifmt_ctx, pkt);
if (ret < 0)
{
std::cout << "读取到文件末尾!" << std::endl;
break;
}
in_stream = ifmt_ctx->streams[pkt->stream_index];
if (stream_mapping[pkt->stream_index] < 0) {
av_packet_unref(pkt);
continue;
}
if (pkt->stream_index==videostream&& need_filter == true)
{
//Use av_bsf_send_packet() and av_bsf_receive_packet()
ret = av_bsf_send_packet(v_ctx, pkt);
if (ret < 0)
{
std::cout << "av_bsf_send_packet failed!" << std::endl;
av_packet_unref(pkt);
continue;
}
av_packet_unref(pkt);
ret = av_bsf_receive_packet(v_ctx, pkt);
if (ret < 0)
{
std::cout << "av_bsf_receive_packet failed!" << std::endl;
av_packet_unref(pkt);
continue;
}
}
pkt->stream_index = stream_mapping[pkt->stream_index];
out_stream = ofmt_ctx->streams[pkt->stream_index];
/* copy packet */
//时间基数转换
//转换PTS/DTS(Convert PTS/DTS)
pkt->pts = av_rescale_q_rnd(pkt->pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
pkt->dts = av_rescale_q_rnd(pkt->dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
pkt->duration = av_rescale_q(pkt->duration, in_stream->time_base, out_stream->time_base);
pkt->pos = -1;
//写入(Write)
ret = av_interleaved_write_frame(ofmt_ctx, pkt);
if (ret < 0) {
std::cout << "av_interleaved_write_frame failed!" << std::endl;
break;
}
frame_index++;
if (frame_index%3000==0)
{
std::cout << "Write frames to output file:" << frame_index << std::endl;
}
//记得引用计数减1,否则会引起内存泄漏。
av_packet_unref(pkt);
}
//写文件尾(Write file trailer)
av_write_trailer(ofmt_ctx);
av_packet_free(&pkt);
end:
av_bsf_free(&v_ctx);
//不在需要释放掉,因为av_bsf_free(&v_ctx)已经在内部把该指针释放掉了。
//avcodec_parameters_free(&p);
delete[] stream_mapping;
avformat_close_input(&ifmt_ctx);
/* close output */
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
avio_closep(&ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
if (ret < 0 && ret != AVERROR_EOF) {
return 1;
}
return 0;
}
/*
@USER wangyu
@DATE 2020-08-06
@VERSION 1.0
*/
#include
extern "C"
{
#include
#include
}
int main(int argc,char *argv[])
{
//输出格式
AVOutputFormat* ofmt = NULL;
//输入于输出媒体句柄。
AVFormatContext* ifmt_ctx = NULL, * ofmt_ctx = NULL;
AVPacket* pkt = NULL;
//输入输出文件名字。
const char* in_filename, * out_filename;
/*in_filename = "test.mp4";
out_filename = "test1.mov";*/
int ret = -1;
//用于存储输入流的索引。
int* stream_mapping = NULL;
int stream_index = 0;
int frame_index = 0;
if (argc < 2)
{
std::cout << "请键入输入文件和输出文件参数重新执行该程序!" << std::endl;
return -1;
}
in_filename = argv[1];
out_filename = argv[2];
//打开源文件。
ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0);
if (ret < 0)
{
std::cout << "could not open the filename :" << in_filename << std::endl;
goto end;
}
else {
std::cout << "open the filename :" << in_filename << "success" << std::endl;
}
ret = avformat_find_stream_info(ifmt_ctx, 0);
if (ret < 0) {
std::cout << "avformat_find_stream_info filed!" << std::endl;
goto end;
}
//打印详细信息。
av_dump_format(ifmt_ctx, 0, in_filename, 0);
stream_mapping = new int[ifmt_ctx->nb_streams];
//为输出的media handle申请空间
avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);
if (!ofmt_ctx) {
std::cout << "Could not create output context!" << std::endl;
avformat_close_input(&ifmt_ctx);
goto end;
}
ofmt = ofmt_ctx->oformat;
for (int i = 0; i < ifmt_ctx->nb_streams; i++)
{
//根据输入流创建输出流(Create output AVStream according to input AVStream)
AVStream* in_stream = ifmt_ctx->streams[i];
AVCodecParameters* in_codecpar = in_stream->codecpar;
AVStream* out_stream = NULL;
if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&
in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
{
//这里将丢弃的流值存为-1,方便后续读帧时丢弃。
stream_mapping[i] = -1;
continue;
}
//将要保留的媒体流进行索引存储。
stream_mapping[i] = stream_index;
stream_index++;
//新建一个媒体流
out_stream = avformat_new_stream(ofmt_ctx, NULL);
if (!out_stream)
{
std::cout << "avformat_new_stream failed" << std::endl;
avformat_close_input(&ifmt_ctx);
goto end;
}
//参数拷贝
ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);
if (ret < 0)
{
std::cout << "avcodec_parameters_copy filed!" << std::endl;
goto end;
}
out_stream->codecpar->codec_tag = 0;
}
//输出一下格式------------------
av_dump_format(ofmt_ctx, 0, out_filename, 1);
//打开输出文件(Open output file)
if (!(ofmt->flags & AVFMT_NOFILE)) {
ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);
if (ret < 0) {
std::cout << "could not open the outputfile:" << out_filename << std::endl;
goto end;
}
}
//写文件头(Write file header)
ret = avformat_write_header(ofmt_ctx, NULL);
if (ret < 0) {
std::cout << "avformat_write_header failed!" << std::endl;
goto end;
}
pkt = av_packet_alloc();
while (1)
{
AVStream* in_stream=NULL;
AVStream* out_stream=NULL;
//获取pkt
ret = av_read_frame(ifmt_ctx, pkt);
if (ret < 0)
{
std::cout << "读取到文件末尾!" << std::endl;
break;
}
in_stream = ifmt_ctx->streams[pkt->stream_index];
if (stream_mapping[pkt->stream_index] < 0) {
av_packet_unref(pkt);
continue;
}
pkt->stream_index = stream_mapping[pkt->stream_index];
out_stream = ofmt_ctx->streams[pkt->stream_index];
/* copy packet */
//时间基数转换
//转换PTS/DTS(Convert PTS/DTS)
pkt->pts = av_rescale_q_rnd(pkt->pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
pkt->dts = av_rescale_q_rnd(pkt->dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
pkt->duration = av_rescale_q(pkt->duration, in_stream->time_base, out_stream->time_base);
pkt->pos = -1;
//写入(Write)
ret = av_interleaved_write_frame(ofmt_ctx, pkt);
if (ret < 0) {
std::cout << "av_interleaved_write_frame failed!" << std::endl;
break;
}
frame_index++;
if (frame_index%1000==0)
{
std::cout << "Write frames to output file:" << frame_index << std::endl;
}
//记得引用计数减1,否则会引起内存泄漏。
av_packet_unref(pkt);
}
//写文件尾(Write file trailer)
end:
delete[] stream_mapping;
avformat_close_input(&ifmt_ctx);
av_write_trailer(ofmt_ctx);
av_packet_free(&pkt);
avformat_close_input(&ifmt_ctx);
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
avio_close(ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
if (ret < 0 && ret != AVERROR_EOF) {
printf("Error occurred.\n");
return -1;
}
return 0;
}