【FFmpeg4.1.4】音视频分离器
                                                            生活随笔
收集整理的這篇文章主要介紹了
                                【FFmpeg4.1.4】音视频分离器
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.                        
                                音視頻分離器
- 一、MP4提取H265裸流無效
- 二、添加頭信息
- 三、音視頻分離步驟
- 1、簡單版本(不支持音頻AAC)
- 2、標準版源碼(支持音頻AAC)
 
- 四、命令行
 
一、MP4提取H265裸流無效
FFmpeg解封裝得到的AVPacket只包含了視頻壓縮數據,沒有相關的參數集信息(比如:h265的vps頭信息,h264的sps、pps頭信息,AAC的adts頭信息),不能初始化解碼器。
二、添加頭信息
StartCodePrefix的兩種方式: Annex B和HVCC。
MP4編碼方式,Nalu開始四個字節表示數據長度。在FFmpeg中,這些頭信息(VPS、SPS、PPS)是保存在解碼器上下文(AVCodecContext)的extradata中的,所以我們需要為每一種格式的視頻添加相應的解碼頭信息,這樣解碼器(MediaCodec)才能正確解析每一個AVPacket里的視頻數據。FFmpeg提供了AVBitStreamFilter類實現頭信息添加的功能。
三、音視頻分離步驟
1、簡單版本(不支持音頻AAC)
#include <iostream> #include <string> #ifdef __cplusplus extern "C" { #endif // __cplusplus#include <libavformat/avformat.h> #include <libavcodec/avcodec.h> #include <libavutil/opt.h> #include <libavutil/imgutils.h>#ifdef __cplusplus } #endif // __cplusplusint demux(std::string mediafile) {const AVBitStreamFilter *filter = NULL;AVFormatContext *fmt_ctx = NULL;AVBSFContext *bsf_ctx;AVPacket pkt;AVStream* videostream = NULL;AVStream* audiostream = NULL;AVCodecID video_codec_id;AVCodecID audio_codec_id;FILE *fp_vides = NULL, *fp_audes = NULL;int i, vid_idx, aud_idx;std::string videofile;std::string audiofile;if (avformat_open_input(&fmt_ctx, mediafile.c_str(), NULL, NULL) != 0) {av_log(NULL, AV_LOG_ERROR, "Could not open input file");exit(1);}av_dump_format(fmt_ctx, 0, mediafile.c_str(), 0);if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {avformat_free_context(fmt_ctx);av_log(NULL, AV_LOG_ERROR, "Failed to retrieve input stream information");exit(1);}for (i = 0; i<fmt_ctx->nb_streams; i++){//vidoeif (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {vid_idx = i;videostream = fmt_ctx->streams[vid_idx];video_codec_id = videostream->codecpar->codec_id;}//audioelse if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {aud_idx = i;audiostream = fmt_ctx->streams[aud_idx];audio_codec_id = audiostream->codecpar->codec_id;}//such as subtitileelse {//To DO}}if (videostream) {switch (video_codec_id) {case AV_CODEC_ID_H264:filter = av_bsf_get_by_name("h264_mp4toannexb");videofile = "video_es.h264";break;case AV_CODEC_ID_HEVC:filter = av_bsf_get_by_name("hevc_mp4toannexb");videofile = "video_es.h265";break;default:av_log(NULL, AV_LOG_ERROR, "Unkonw Video AVCodecID");exit(1);}fp_vides = fopen(videofile.c_str(), "wb");if (!fp_vides) {std::cerr << "Could not open file" << videofile.c_str() << std::endl;exit(1);}} else {av_log(NULL, AV_LOG_ERROR, "Could not find video stream in the input, aborting");}if (audiostream) {switch (audio_codec_id) {case AV_CODEC_ID_AAC:audiofile = "audio_es.aac";break;case AV_CODEC_ID_AC3:audiofile = "audio_es.aac";break;case AV_CODEC_ID_MP3:audiofile = "audio_es.mp3";break;default:av_log(NULL, AV_LOG_ERROR, "Unkonw Audio AVCodecID");}fp_audes = fopen(audiofile.c_str(), "wb");if (!fp_audes) {std::cerr << "Could not open file" << audiofile.c_str() << std::endl;exit(1);}} else {av_log(NULL, AV_LOG_ERROR, "Could not find audio in the input, aborting");}if (!filter){av_log(NULL, AV_LOG_ERROR, "Unkonw bitstream filter");exit(1);}if (av_bsf_alloc(filter, &bsf_ctx) < 0) {av_log(NULL, AV_LOG_ERROR, "Failed to alloc filter");exit(1);}//拷貝參數集信息avcodec_parameters_copy(bsf_ctx->par_in, fmt_ctx->streams[vid_idx]->codecpar);//初始化過濾器上下文av_bsf_init(bsf_ctx);while (av_read_frame(fmt_ctx, &pkt) >= 0){if (pkt.stream_index == vid_idx) {if (av_bsf_send_packet(bsf_ctx, &pkt) < 0) {std::cerr << "failed to push" << std::endl;exit(1);} while (av_bsf_receive_packet(bsf_ctx, &pkt) == 0);fwrite(pkt.data, pkt.size, 1, fp_vides);} else if (pkt.stream_index == aud_idx)fwrite(pkt.data, pkt.size, 1, fp_audes);else;// such as subtitileav_packet_unref(&pkt);}if (fp_vides) {fclose(fp_vides);fp_vides = NULL;}if (fp_audes) {fclose(fp_audes);fp_audes = NULL;} avformat_close_input(&fmt_ctx);av_bsf_free(&bsf_ctx);return 0; }int main(int argc, char* argv[]) {if (argc < 2) {printf("usage: %s input\n""API example program to demux a media file.\n""The output format is guessed according to the file extension.\n""\n", argv[0]);return 1;}std::string mediafile = argv[1];simple_demux(mediafile); }2、標準版源碼(支持音頻AAC)
void standard_demux(std::string mediafile) {std::string in_filename = mediafile;std::string audio_outfilename;std::string video_outfilename;int point_pos = in_filename.find_last_of('.');std::string prefix_filename = in_filename.substr(0, point_pos + 1);AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx_a = NULL, *ofmt_ctx_v = NULL;FILE *fp_audes = NULL;AVPacket pkt;AVCodecID video_codec_id;AVCodecID audio_codec_id;int ret = 0, i;int video_idx = -1;int audio_idx = -1;//打開輸入文件if ((avformat_open_input(&ifmt_ctx, in_filename.c_str(), 0, 0)) < 0) {av_log(NULL, AV_LOG_ERROR, "Could not open input file");goto end;}for (i = 0; i < ifmt_ctx->nb_streams; i++) {//videoif (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {video_idx = i;video_codec_id = ifmt_ctx->streams[video_idx]->codecpar->codec_id;switch (video_codec_id) {case AV_CODEC_ID_H264:video_outfilename = prefix_filename + "h264";break;case AV_CODEC_ID_HEVC:video_outfilename = prefix_filename + "h265";break;default:av_log(NULL, AV_LOG_ERROR, "Unkonw Video AVCodecID");goto end;}}//audioelse if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {audio_idx = i;audio_codec_id = ifmt_ctx->streams[audio_idx]->codecpar->codec_id;switch (audio_codec_id) {case AV_CODEC_ID_AAC:audio_outfilename = prefix_filename + "aac";break;case AV_CODEC_ID_AC3:audio_outfilename = prefix_filename + "ac3";break;case AV_CODEC_ID_MP3:audio_outfilename = prefix_filename + "mp3";break;default:av_log(NULL, AV_LOG_ERROR, "Unkonw Audio AVCodecID");goto end;}}//such as subtitileelse {//To DO}}av_dump_format(ifmt_ctx, 0, mediafile.c_str(), 0);if (video_idx == -1) {av_log(NULL, AV_LOG_ERROR, "%s do not have no video stream", in_filename.c_str());}if (audio_idx == -1) {av_log(NULL, AV_LOG_ERROR, "%s do not have no audio stream", in_filename.c_str());}if (video_idx == -1 && audio_idx == -1) {goto end;}if ((avformat_find_stream_info(ifmt_ctx, 0)) < 0) {avformat_free_context(ifmt_ctx);av_log(NULL, AV_LOG_ERROR, "Failed to retrieve input stream information");goto end;}//打開輸出視頻文件if (video_idx != -1 && (avformat_alloc_output_context2(&ofmt_ctx_v, NULL, NULL, video_outfilename.c_str())) < 0) {av_log(NULL, AV_LOG_ERROR, "could not allocate video output file");goto end;}//打開輸出音頻文件if (audio_idx != -1) {if (avformat_alloc_output_context2(&ofmt_ctx_a, NULL, NULL, audio_outfilename.c_str()) < 0) {av_log(NULL, AV_LOG_ERROR, "could not allocate audio output file");goto end;}}for (i = 0; i < ifmt_ctx->nb_streams; i++) {AVStream *out_stream = NULL;//videoif (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {if ((out_stream = avformat_new_stream(ofmt_ctx_v, NULL)) < 0) {av_log(NULL, AV_LOG_ERROR, "could not allocate output stream");goto end;};if (avcodec_parameters_copy(out_stream->codecpar, ifmt_ctx->streams[i]->codecpar) < 0) {av_log(NULL, AV_LOG_ERROR, "Failed to copy context from input to output video stream codec context");goto end;}out_stream->codecpar->codec_tag = 0;}//audioelse if (ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {if (ifmt_ctx->streams[i]->codecpar->codec_id != AV_CODEC_ID_AAC && ifmt_ctx->streams[i]->codecpar->codec_id != AV_CODEC_ID_AC3)continue;if ((out_stream = avformat_new_stream(ofmt_ctx_a, NULL)) < 0) {av_log(NULL, AV_LOG_ERROR, "could not allocate output stream");goto end;};if (avcodec_parameters_copy(out_stream->codecpar, ifmt_ctx->streams[i]->codecpar) < 0) {av_log(NULL, AV_LOG_ERROR, "Failed to copy context from input to output audio stream codec context");goto end;}out_stream->codecpar->codec_tag = 0;}//such as subtitileelse {//To DO}} if (video_idx != -1) {if (avio_open(&ofmt_ctx_v->pb, video_outfilename.c_str(), AVIO_FLAG_WRITE) < 0) {av_log(NULL, AV_LOG_ERROR, "Could not open output file %s", video_outfilename);goto end;}//Write file headerif (avformat_write_header(ofmt_ctx_v, NULL) < 0) {av_log(NULL, AV_LOG_ERROR, "Error occurred when opening video output file");goto end;}}if (audio_idx != -1) {if (ofmt_ctx_a->streams[0]->codecpar->codec_id == AV_CODEC_ID_AAC) {if (avio_open(&ofmt_ctx_a->pb, audio_outfilename.c_str(), AVIO_FLAG_WRITE) < 0) {av_log(NULL, AV_LOG_ERROR, "Could not open output file %s", audio_outfilename);goto end;}if (avformat_write_header(ofmt_ctx_a, NULL) < 0) {av_log(NULL, AV_LOG_ERROR, "Error occurred when opening audio output file");goto end;}} else {fp_audes = fopen(audio_outfilename.c_str(), "wb");if (!fp_audes) {av_log(NULL, AV_LOG_ERROR, "Could not open file", audio_outfilename.c_str());exit(1);}}}while (av_read_frame(ifmt_ctx, &pkt) >= 0) {AVFormatContext *ofmt_ctx;AVStream *in_stream, *out_stream;in_stream = ifmt_ctx->streams[pkt.stream_index];if (pkt.stream_index == video_idx) {out_stream = ofmt_ctx_v->streams[0];ofmt_ctx = ofmt_ctx_v;printf("Write Video Packet. size:%d\tpts:%lld\n", pkt.size, pkt.pts);}else if (pkt.stream_index == audio_idx) {if (ifmt_ctx->streams[audio_idx]->codecpar->codec_id == AV_CODEC_ID_AAC) {out_stream = ofmt_ctx_a->streams[0];ofmt_ctx = ofmt_ctx_a;printf("Write Audio Packet. size:%d\tpts:%lld\n", pkt.size, pkt.pts);}else {fwrite(pkt.data, pkt.size, 1, fp_audes);av_packet_unref(&pkt);printf("Write Audio Packet. size:%d\tpts:%lld\n", pkt.size, pkt.pts);continue;}}else {continue;}//Convert PTS/DTSpkt.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;pkt.stream_index = 0;//Writeif (av_interleaved_write_frame(ofmt_ctx, &pkt) < 0) {av_log(NULL, AV_LOG_ERROR, "Error muxing packet");break;}av_packet_unref(&pkt);}//Write file trailerif (ofmt_ctx_v) {av_write_trailer(ofmt_ctx_v);} if (ofmt_ctx_a) {av_write_trailer(ofmt_ctx_a);} end:avformat_close_input(&ifmt_ctx);if (ofmt_ctx_v && !(ofmt_ctx_v->flags & AVFMT_NOFILE)){avio_close(ofmt_ctx_v->pb);}avformat_free_context(ofmt_ctx_v);if (ret < 0 && ret != AVERROR_EOF) {printf("Error occurred.\n");system("pause");exit(1);}if (ofmt_ctx_a && !(ofmt_ctx_a->flags & AVFMT_NOFILE)){avio_close(ofmt_ctx_a->pb);} avformat_free_context(ofmt_ctx_a);if (ret < 0 && ret != AVERROR_EOF) {printf("Error occurred.\n");system("pause");exit(1);}if (fp_audes) {fclose(fp_audes);fp_audes = NULL;} }int main(int argc, char* argv[]) {if (argc < 2) {printf("usage: %s input\n""API example program to demux a media file.\n""The output format is guessed according to the file extension.\n""\n", argv[0]);return 1;}std::string mediafile = argv[1];standard_demux(mediafile); }四、命令行
FFmpeg命令,從MP4中提取h265視頻流
ffmpeg -i input.mp4 -codec copy -bsf: hevc_mp4toannexb -f hevc out.h265-i input.mp4: 是輸入的MP4文件
 -codec copy: 從mp4中拷貝
 -bsf: hevc_mp4toannexb: 從mp4拷貝到annexB封裝
 -f hevc: 采用hevc格式
 out.h265: 輸出的文件
總結
以上是生活随笔為你收集整理的【FFmpeg4.1.4】音视频分离器的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 用javacv提取视频中的音频数据
- 下一篇: 弹性波波长计算公式_弹性波,时域显式接口
