介绍
接下去两篇文章介绍下通过qt+ffmpeg抽取音视频,然后单独播放出来。首先需要了解的ffmpeg的几个api。
抽取音频api介绍
av_init_packet():初始化一个数据包结构体,我们从多媒体文件中读取了每一个数据包,都可以放在被初始化的包结构体里。
av_find_best_stream(): 通过这个名字就能猜到,是在多媒体文件中找到最好的一路流,
av_read_frame(): 获取流中的数据包,获取到数据包之后就可以做一些逻辑处理。在这个例子中我们就会写入到aac文件中。
av_packet_unref() : packet 引用计数减一,ffmpeg观察到引用计数位0 就会释放相应资源。要是忘记了的话就会造成内存泄露。
avformat_alloc_context(): 分配一个空的 AVFormatContext 结构体的函数。在 FFmpeg 中,AVFormatContext 结构体用于表示音视频格式的上下文信息,包括输入/输出文件的格式、编解码器、流信息等。
av_guess_format(): 函数用于根据文件名猜测输出文件的格式。具体来说,它会根据文件名的后缀(例如 “.mp4”、“.avi” 等)来猜测文件的格式,然后返回相应的输出格式。如果无法确定文件格式,则返回 NULL。
avcodec_parameters_copy():函数用于将一个 AVCodecParameters 结构体的内容复制到另一个 AVCodecParameters 结构体中。这个函数通常用于在创建新的输出流时,从输入流中复制编解码器参数到输出流中,以确保输出流的编解码器参数与输入流相同。
avio_open():函数会根据提供的路径和选项打开文件,并创建一个 AVIOContext 结构体,以便后续的读写操作。在成功打开文件后,可以使用相关的 avio_read()、avio_write() 等函数进行数据的读写操作。
avformat_write_header() : 函数用于写入输出文件的文件头信息。在对输出文件进行写入操作之前,必须先调用该函数来写入文件的头部信息,以确保输出文件的格式正确并且包含所需的元数据。
av_write_trailer() : 用于写入输出文件的尾部信息。在完成对输出文件的所有写入操作后,必须调用该函数来完成输出文件的写入过程,并且确保尾部信息正确写入。
avio_close(): 用于关闭由 avio_open() 打开的 IO 上下文(AVIOContext)。在完成对文件的写入或读取操作后,应该调用此函数来关闭文件。
以上基本列出了所有需要使用的api,忽略了参数部分。
接下来就开始提取mp4中的音频数据了,我这边的demo直接写在了主窗口的构造函数往下写了,没有用一些拖动event,后面会做修改。把提取音频,提取视频,音视频合并放在一个项目中。这里直接贴上代码了 ,输入文件输出文件自己改动下。
int MainWindow::ExtraAACFFmpeg() {
int err_code;
char errors[1024];
char *src_filename = "C:/Users/57102/Desktop/test/tttt.mp4";
char *dst_filename ="C:/Users/57102/Desktop/test/ttss.aac";
FILE *dst_fd = NULL;
int audio_stream_index = -1;
int len;
AVFormatContext *ofmt_ctx = NULL;
AVOutputFormat *output_fmt = NULL;
AVStream *in_stream = NULL;
AVStream *out_stream = NULL;
AVFormatContext *fmt_ctx = NULL;
//AVFrame *frame = NULL;
AVPacket pkt;
av_log_set_level(AV_LOG_DEBUG);
if(src_filename == NULL || dst_filename == NULL){
av_log(NULL, AV_LOG_DEBUG, "src or dts file is null, plz check them!\n");
return -1;
}
av_register_all();
/*open input media file, and allocate format context*/
if((err_code = avformat_open_input(&fmt_ctx, src_filename, NULL, NULL)) < 0){
av_strerror(err_code, errors, 1024);
av_log(NULL, AV_LOG_DEBUG, "Could not open source file: %s, %d(%s)\n",
src_filename,
err_code,
errors);
return -1;
}
/*retrieve audio stream*/
if((err_code = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
av_strerror(err_code, errors, 1024);
av_log(NULL, AV_LOG_DEBUG, "failed to find stream information: %s, %d(%s)\n",
src_filename,
err_code,
errors);
return -1;
}
/*dump input information*/
av_dump_format(fmt_ctx, 0, src_filename, 0);
in_stream = fmt_ctx->streams[1];
AVCodecParameters *in_codecpar = in_stream->codecpar;
if(in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO){
av_log(NULL, AV_LOG_ERROR, "The Codec type is invalid!\n");
exit(1);
}
//out file
ofmt_ctx = avformat_alloc_context();
output_fmt = av_guess_format(NULL, dst_filename, NULL);
if(!output_fmt){
av_log(NULL, AV_LOG_DEBUG, "Cloud not guess file format \n");
exit(1);
}
ofmt_ctx->oformat = output_fmt;
out_stream = avformat_new_stream(ofmt_ctx, NULL);
if(!out_stream){
av_log(NULL, AV_LOG_DEBUG, "Failed to create out stream!\n");
exit(1);
}
if(fmt_ctx->nb_streams<2){
av_log(NULL, AV_LOG_ERROR, "the number of stream is too less!\n");
exit(1);
}
if((err_code = avcodec_parameters_copy(out_stream->codecpar, in_codecpar)) < 0 ){
// av_strerror(err_code, errors, ERROR_STR_SIZE);
av_log(NULL, AV_LOG_ERROR,
"Failed to copy codec parameter, %d(%s)\n",
err_code, errors);
}
out_stream->codecpar->codec_tag = 0;
if((err_code = avio_open(&ofmt_ctx->pb, dst_filename, AVIO_FLAG_WRITE)) < 0) {
av_strerror(err_code, errors, 1024);
av_log(NULL, AV_LOG_DEBUG, "Could not open file %s, %d(%s)\n",
dst_filename,
err_code,
errors);
exit(1);
}
/*dump output information*/
av_dump_format(ofmt_ctx, 0, dst_filename, 1);
/*initialize packet*/
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
/*find best audio stream*/
audio_stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
if(audio_stream_index < 0){
av_log(NULL, AV_LOG_DEBUG, "Could not find %s stream in input file %s\n",
av_get_media_type_string(AVMEDIA_TYPE_AUDIO),
src_filename);
return AVERROR(EINVAL);
}
if (avformat_write_header(ofmt_ctx, NULL) < 0) {
av_log(NULL, AV_LOG_DEBUG, "Error occurred when opening output file");
exit(1);
}
/*read frames from media file*/
while(av_read_frame(fmt_ctx, &pkt) >=0 ){
if(pkt.stream_index == audio_stream_index){
//(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX)
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AV_ROUND_NEAR_INF ));
pkt.dts = pkt.pts;
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
pkt.pos = -1;
pkt.stream_index = 0;
av_interleaved_write_frame(ofmt_ctx, &pkt);
av_packet_unref(&pkt);
}
}
av_write_trailer(ofmt_ctx);
/*close input media file*/
avformat_close_input(&fmt_ctx);
if(dst_fd) {
fclose(dst_fd);
}
avio_close(ofmt_ctx->pb);
return 0;
}
总结
用ffmpeg提供的api还是比较简单的,但是你要想挑战下自己 ,都是自己手写,比如adts头这种就不是很简单了。需要对着文档,每个bit位都都需要注意。