1. 程式人生 > >ffmpeg4教程4:採集桌面聲音壓縮為aac

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