ffmpeg4教程4:採集桌面聲音壓縮為aac
基於vs2017 vc++ ffmpeg4.0.2下測試
ffmpeg 環境配置請百度(vs2017 ffmpeg )影象和聲音請安裝dshow便於查詢
部分方法在https://blog.csdn.net/Java_lilin/article/details/85118365中查詢
標頭檔案參考上篇教程
static int test5() {
const char* filename = "C:\\Users\\lilin\\Desktop\\1.aac";
AVFormatContext *oc= NULL;
avformat_alloc_output_context2(&oc,NULL,NULL, filename);
if (oc == NULL) {
printf("AVFormatContext init fail");
return -1;
}
AVOutputFormat *fmt = oc->oformat;
fmt->audio_codec = AV_CODEC_ID_AAC;//AV_CODEC_ID_MP3 mp3格式 改下面4個
AVCodec *encodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
if (!encodec) {
printf("not found encoder for aac");
avcodec_get_name(AV_CODEC_ID_AAC);
return -1;
}
AVStream *st = avformat_new_stream(oc, encodec);
st->id = oc->nb_streams - 1;
/*******************************/
//test4 程式碼
AVCodecContext *enctx = avcodec_alloc_context3(encodec);
enctx->codec_id = AV_CODEC_ID_AAC;
enctx->sample_fmt = encodec->sample_fmts ? encodec->sample_fmts[0] : AV_SAMPLE_FMT_FLT;
enctx->bit_rate = 128000;
enctx->sample_rate = 44100;
if (encodec->supported_samplerates) {//查詢是否支援
enctx->sample_rate = encodec->supported_samplerates[0];
for (int i = 0; encodec->supported_samplerates[i]; i++)
{
if (encodec->supported_samplerates[i] == 44100) { enctx->sample_rate = 44100; }
}
}
enctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
enctx->channel_layout = AV_CH_LAYOUT_STEREO;
if (encodec->channel_layouts) {//查詢是否支援
enctx->channel_layout = encodec->channel_layouts[0];
for (int i = 0; encodec->channel_layouts[i]; i++)
{
if (encodec->channel_layouts[i] == AV_CH_LAYOUT_STEREO) { enctx->channel_layout = AV_CH_LAYOUT_STEREO; }
}
}
enctx->channels = av_get_channel_layout_nb_channels(enctx->channel_layout);
//播放器播放不出來
if (fmt->flags&AVFMT_GLOBALHEADER) {
enctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
AVDictionary* opt = NULL;
int ret = avcodec_open2(enctx, encodec, &opt);
av_dict_free(&opt);
if (ret < 0) {
printf("not open audio codec");
return -1;
}
//add關聯 把編輯器引數傳遞給輸出流
avcodec_parameters_from_context(st->codecpar,enctx);
av_dump_format(oc,0,filename,1);//列印輸出流
//開啟寫頭資訊
if (!(fmt->flags&AVFMT_NOFILE)) {
ret = avio_open(&oc->pb,filename,AVIO_FLAG_WRITE);
if (ret < 0) {
printf("無法輸出到指定位置");
return -1;
}
}
/*write stram header if any*/
opt = NULL;
ret = avformat_write_header(oc,&opt);
av_dict_free(&opt);
if (ret < 0) {
printf("open output file error");
return -1;
}
//test2 相同程式碼
AVFormatContext *formatCtx = avformat_alloc_context();
AVInputFormat *ifmt = av_find_input_format("dshow");//裝置型別
AVDictionary* options = NULL;
//av_dict_set(&options, "video_size","1920*1080",0);//大小 預設全部
av_dict_set(&options, "framerate", "15", 0);//幀lu
if (avformat_open_input(&formatCtx, "audio=virtual-audio-capturer", ifmt, &options) != 0) {
printf("open input device fail\n");
return -1;
}
av_dict_free(&options);
if (avformat_find_stream_info(formatCtx, NULL) < 0) {
printf("avformat_find_stream_info faill\n");
return -1;
}
if (formatCtx->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) {
printf("no find stream info\n");
return -1;
}
//查詢解密器
AVCodec *codec = avcodec_find_decoder(formatCtx->streams[0]->codecpar->codec_id);
if (codec == NULL) {
printf("codec not found\n");
return -1;
}
AVCodecContext *ctx = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(ctx, formatCtx->streams[0]->codecpar);
if (avcodec_open2(ctx, codec, NULL) < 0) {
printf("codec not open\n");
return -1;
}
AVFrame *frame = alloc_audio_frame((AVSampleFormat)formatCtx->streams[0]->codecpar->format, formatCtx->streams[0]->codecpar->channel_layout, formatCtx->streams[0]->codecpar->channels, formatCtx->streams[0]->codecpar->sample_rate, formatCtx->streams[0]->codecpar->frame_size);
//目標
ctx->channel_layout = av_get_default_channel_layout(ctx->channels);
SwrContext *swr = swr_alloc();
av_opt_set_int(swr, "in_channel_count", ctx->channels, 0);
av_opt_set_int(swr, "in_channel_layout", ctx->channel_layout, 0);
av_opt_set_int(swr, "in_sample_rate", ctx->sample_rate, 0);
av_opt_set_sample_fmt(swr, "in_sample_fmt", ctx->sample_fmt, 0);
//gai
int64_t desc_channel_layout = enctx->channel_layout;
int64_t desc_channel_count = enctx->channels;
int64_t desc_sample_rate = enctx->sample_rate;
AVSampleFormat desc_fmt = enctx->sample_fmt;
av_opt_set_int(swr, "out_channel_count", desc_channel_count, 0);
av_opt_set_int(swr, "out_channel_layout", desc_channel_layout, 0);
av_opt_set_int(swr, "out_sample_rate", desc_sample_rate, 0);
av_opt_set_sample_fmt(swr, "out_sample_fmt", desc_fmt, 0);
printf("\n聲道%d,取樣率%d格式%d----》聲道%d,取樣率%d格式%d\n", ctx->channels, ctx->sample_rate,
ctx->sample_fmt, desc_channel_count, desc_sample_rate, desc_fmt);
int DES_NB_SAMPLES = enctx->frame_size;//每次需要1024個 不夠有噪音
AVFrame *outframe = alloc_audio_frame(desc_fmt, desc_channel_layout, desc_channel_count, desc_sample_rate, DES_NB_SAMPLES);
AVFrame *newframe = alloc_audio_frame(desc_fmt, desc_channel_layout, desc_channel_count, desc_sample_rate, 0);
AVFrame *newframe2 = alloc_audio_frame(desc_fmt, desc_channel_layout, desc_channel_count, desc_sample_rate, 0);
AVAudioFifo *fifo = av_audio_fifo_alloc(desc_fmt, desc_channel_count, 10240);
//FILE *file = NULL;
//fopen_s(&file, "C:\\Users\\lilin\\Desktop\\1.acc", "wb");//ffplay 1.aac
int fnum = 1000;
int64_t nexttps = 0;
while (fnum--) {
int size = av_audio_fifo_size(fifo);
if (size >= DES_NB_SAMPLES) {//超過
printf("\n第一次超過\n");
size = DES_NB_SAMPLES;
av_audio_fifo_read(fifo, (void**)outframe->data, size);
//完整
//寫檔案 替換
if (nexttps == 0) { outframe->pts = av_gettime(); }
else { outframe->pts = nexttps; }
nexttps = outframe->pts + 1000000LL * outframe->linesize[0] / (enctx->sample_rate*av_get_bytes_per_sample(enctx->sample_fmt) );
{
AVRational base; base.num = 1; base.den = 1000000LL;//幀率
//轉換回去去壓縮器的時基
outframe->pts = av_rescale_q(outframe->pts, base, enctx->time_base);
}
//開始壓縮
avcodec_send_frame(enctx, outframe);
AVPacket pkt = { 0 }; av_init_packet(&pkt);
ret = avcodec_receive_packet(enctx, &pkt);
if (ret == 0) {// 成功
//char *p=addADTStoPacket(file, pkt.size);
//fwrite(pkt.data, pkt.size, 1, file);
write_frame(oc, &enctx->time_base,st,&pkt);
}
av_packet_unref(&pkt);
continue;
}
AVPacket packet = { 0 };
av_init_packet(&packet);
if (av_read_frame(formatCtx, &packet) >= 0) {
avcodec_send_packet(ctx, &packet);
if (avcodec_receive_frame(ctx, frame) < 0) {
printf("audio decode error\n");
}
else {
printf("採集到音訊"); //
swr_convert_frame(swr, newframe, frame);
av_audio_fifo_write(fifo, (void**)newframe->data, newframe->nb_samples);
int64_t dealy = swr_get_delay(swr, desc_sample_rate);
if (dealy > 0) {
swr_convert_frame(swr, newframe2, NULL);
av_audio_fifo_write(fifo, (void**)newframe2->data, newframe2->nb_samples);
}
//這裡可以判斷下fifo裡的數量是否夠1024
int size = av_audio_fifo_size(fifo);
if (size < DES_NB_SAMPLES) {
//資料不夠
av_packet_unref(&packet);
continue;
}
size = DES_NB_SAMPLES;//夠了
av_audio_fifo_read(fifo, (void**)outframe->data, size);//固定大小的
//寫檔案 gai
//fwrite(outframe->data[0], size, desc_channel_count*av_get_bytes_per_sample(desc_fmt), file);
if (nexttps == 0) { outframe->pts = av_gettime(); }
else { outframe->pts = nexttps; }
//.nexttps = outframe->pts + 1000000LL * enctx->time_base.num / enctx->time_base.den;
nexttps = outframe->pts + 1000000LL * outframe->linesize[0] / (enctx->sample_rate*av_get_bytes_per_sample(enctx->sample_fmt));
{
AVRational base; base.num = 1; base.den = 1000000LL;//幀率
//轉換回去去壓縮器的時基
outframe->pts = av_rescale_q(outframe->pts, base, enctx->time_base);
}
//開始壓縮
avcodec_send_frame(enctx, outframe);
AVPacket pkt = { 0 };
av_init_packet(&pkt);
ret = avcodec_receive_packet(enctx, &pkt);
if (ret == 0) {//
//壓縮成功
// char *p=addADTStoPacket(file, pkt.size);
//fwrite(pkt.data, pkt.size, 1, file);
write_frame(oc, &enctx->time_base, st, &pkt);
}
av_packet_unref(&pkt);
}
}
av_packet_unref(&packet);
}
//關閉liou
av_write_trailer(oc);
av_frame_free(&frame);
av_frame_free(&newframe);
av_frame_free(&newframe2);
av_frame_free(&outframe);
av_audio_fifo_free(fifo);
avcodec_free_context(&ctx);
avformat_close_input(&formatCtx);
avcodec_free_context(&enctx);
//關閉liou
avformat_close_input(&oc);
return 0;
}
main{
printf("ok:%d\n", avcodec_version());
avdevice_register_all();
test5();
}
討論群261074724