(20条消息) FFmpeg + Visual studio 开发环境搭建_HW140701的博客-CSDN博客
1.封装格式:AVI,MP4,ASF
AVI:压缩标准可以任意选
FLV,ts:直播等使用的流媒体
mp4:既是封装又是压缩
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avutil.lib")
int main(int argc, char *argv[])
{
char infile[] = "test.mp4";//要转的文件
char outfile[] = "test.mov";//目标文件
av_register_all();//注册了很多东西
/*AVFormatContext **ps整个封装格式上下文
{
AVIOContext *pb;IO上下文,通过他可以进行写入
AVStream **streams;视频音频字幕流,存放了所有的流的信息,一般这个数组的第一个是视频,第二个是音频
}
const char * url音视频文件地址
AVInputFormat *fmt音视频格式,此格式可以传NULL,由url决定
AVDictionary **options音视频格式的参数
*/
AVFormatContext* ic = NULL;
/**
* 打开输入流并读取标头。编解码器未打开。
* 流必须使用 avformat_close_input() 关闭。
*
* @param ps 指向用户提供的 AVFormatContext 的指针(由 avformat_alloc_context 分配)。
* 可能是指向 NULL 的指针,在这种情况下,AVFormatContext 由此分配
* 函数并写入 PS。
* 请注意,用户提供的 AVFormatContext 将在失败时释放。
* @param要打开的流的网址网址。
* @param fmt 如果非 NULL,则此参数强制使用特定的输入格式。
* 否则会自动检测格式。
* @param选项 一个充满AVFormatContext和demuxer-private选项的字典。
* 返回时,此参数将被销毁并替换为包含
* 未找到的选项。可能为空。
*
* 成功时为 @return 0,失败时为 AVERROR 为负值。
*
* @note 如果要使用自定义 IO,请预分配格式上下文并设置其 pb 字段。
*/
avformat_open_input(&ic, infile, 0, 0);//后两个参数可根据音频文件的后缀自动检测
if (!ic)
{
cout << "avformat_open_input failed!" << endl;
getchar();
}
///2 create output context
AVFormatContext* oc = NULL;
/**
* 为输出格式分配 AVFormatContext。
* avformat_free_context() 可用于释放上下文和
* 其中框架分配的所有内容。
*
* @param *ctx 设置为创建的格式上下文,或在
* 故障案例
* @param用于分配上下文的 oformat 格式,如果为 NULL
* 改用format_name和文件名
* @param format_name用于分配
* 上下文,如果使用 NULL 文件名代替
* @param文件名 用于分配
* 上下文,可能是空的
* @return >= 0 如果成功,则为负 AVERROR 代码
*失败
*/
avformat_alloc_output_context2(&oc, NULL, NULL, outfile);
if (!oc)
{
cerr << "avformat_alloc_output_context2 " << outfile << " failed!" << endl;
getchar();
return -1;
}
///3 add the stream
AVStream* videoStream = avformat_new_stream(oc, NULL);//一般数组的第一个都是视频
AVStream* audioStream = avformat_new_stream(oc, NULL);
///4 copy para
avcodec_parameters_copy(videoStream->codecpar, ic->streams[0]->codecpar);
avcodec_parameters_copy(audioStream->codecpar, ic->streams[1]->codecpar);
videoStream->codecpar->codec_tag = 0;//有关编解码器的其他信息,我们此处不进行使用
audioStream->codecpar->codec_tag = 0;
av_dump_format(ic, 0, infile, 0);//打印输入文件的信息
cout << "================================================" << endl;
av_dump_format(oc, 0, outfile, 1);//打印输出文件的信息
///5 open out file io,write head
int ret = avio_open(&oc->pb, outfile, AVIO_FLAG_WRITE);
if (ret < 0)
{
cerr << "avio open failed!" << endl;
getchar();
return -1;
}
/**
* 分配流私有数据并将流标头写入
* 输出媒体文件。
*
* @param 的媒体文件句柄,必须使用 avformat_alloc_context() 分配。
*其oformat字段必须设置为所需的输出格式;
* 其 pb 字段必须设置为已打开的 AVIOContext。
* @param选项 一个充满 AVFormatContext 和 muxer-private 选项的 AVDictionary。
* 返回时,此参数将被销毁并替换为包含
* 未找到的选项。可能为空。
*
* 如果编解码器尚未在avformat_init中完全初始化,则@return AVSTREAM_INIT_IN_WRITE_HEADER成功,
* 如果编解码器已在 avformat_init 中完全初始化,则AVSTREAM_INIT_IN_INIT_OUTPUT成功,
* 失败时的负面 AVERROR。
*
* @see av_opt_find、av_dict_set、avio_open、av_oformat_next、avformat_init_output。
*/
ret = avformat_write_header(oc, NULL);
if (ret < 0)
{
cerr << "avformat_write_header failed!" << endl;
getchar();
}
AVPacket pkt;
for (;;)
{
int re = av_read_frame(ic, &pkt);//此处注意,因为帧长不一定,所以每次循环需要把之前的数据清理掉
if (re < 0)
break;
/**
* 以 AVStream->time_base 为单位的演示时间戳;时间
* 解压缩的数据包将呈现给用户。
* 如果未存储在文件中,则可以AV_NOPTS_VALUE。
* pts 必须大于或等于 dts,因为演示之前不能发生
*解压缩,除非有人想查看十六进制转储。某些格式滥用
* 术语 DTS 和 PTS/CTS 的含义不同。这样的时间戳
* 在存储在 AVPacket 中之前,必须转换为真正的 pts/dts。
AVPacket:
pts: (int64_t)显示时间,结合AVStream->time_base转换成时间戳
dts : (int64_t)解码时间,结合AVStream->time_base转换成时间戳
size : (int)data的大小
stream_index : (int)packet在stream的index位置
flags : (int)标示,结合AV_PKT_FLAG使用,其中最低为1表示该数据是一个关键帧。
#define AV_PKT_FLAG_KEY 0x0001 //关键帧
#define AV_PKT_FLAG_CORRUPT 0x0002 //损坏的数据
#define AV_PKT_FLAG_DISCARD 0x0004 /丢弃的数据
side_data_elems : (int)边缘数据元数个数
duration : (int64_t)数据的时长,以所属媒体流的时间基准为单位,未知则值为默认值0
pos : (int64_t )数据在流媒体中的位置,未知则值为默认值 - 1
*/
pkt.pts = av_rescale_q_rnd(pkt.pts,
ic->streams[pkt.stream_index]->time_base,
oc->streams[pkt.stream_index]->time_base,
(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)
);
pkt.dts = av_rescale_q_rnd(pkt.dts,
ic->streams[pkt.stream_index]->time_base,
oc->streams[pkt.stream_index]->time_base,
(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)
);
pkt.pos = -1;
pkt.duration = av_rescale_q_rnd(pkt.duration,
ic->streams[pkt.stream_index]->time_base,
oc->streams[pkt.stream_index]->time_base,
(AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)
);
av_write_frame(oc, &pkt);
av_packet_unref(&pkt);
cout << ".";
}
av_write_trailer(oc);//加一个尾字段,类似于记录前面每一帧的索引和总的时长。
avio_close(oc->pb);
cout << "================end================" << endl;
getchar();
return 0;
}