FFmpeg-音频编码会比音频产生更多的噪声

时间:2020-05-17 02:07:43

标签: c audio encoding ffmpeg

我正在尝试使用FFmpeg拍摄视频(在这种情况下为MP4)并将其复制为另一个MP4。这样一来,我就可以摆脱对视频进行解码/编码的烦恼,然后继续执行其他操作。我的代码基本上可以获取视频文件,对视频和音频流进行解码,并将视频和音频流编码为输出视频文件。

到目前为止,我的代码仅适用于输入文件的视频流。输出文件的视频部分与输入文件的视频部分完全相同。但是,音频部分不是。输出的音频部分包含原始音频,但其上有噪音。可以将它想象成有人在尖叫麦克风,或者当声音太大而使扬声器无法处理时。

我处理视频和音频流的解码/编码过程的方式是相同的,除了AVCodecContext设置不同(视频->帧速率,宽度,高度等;音频->采样率) ,渠道等)。

这是我目前正在使用的代码:

视频结构:

typedef struct Video {
    AVFormatContext* inputContext;
    AVFormatContext* outputContext;
    AVCodec* videoCodec;
    AVCodec* audioCodec;
    AVStream* inputStream;
    AVStream* outputStream;
    AVCodecContext* videoCodecContext_I; // Input
    AVCodecContext* audioCodecContext_I; // Input
    AVCodecContext* videoCodecContext_O; // Output
    AVCodecContext* audioCodecContext_O; // Output
    int videoStream; // Video stream index
    int audioStream; // Audio stream index
} Video;

处理编码/解码的主要代码(由于视频端相同,我仅包括音频端):

int openVideo(Video* video, char* filename, char* outputFile) {
    video->inputContext = avformat_alloc_context();
    if (!video->inputContext) {
        printf("[ERROR] Failed to allocate input format context\n");
        return -1;
    }
    if (avformat_open_input(&(video->inputContext), filename, NULL, NULL) < 0) {
        printf("[ERROR] Could not open the input file\n");
        return -1;
    }

    if (avformat_find_stream_info(video->inputContext, NULL) < 0) {
        printf("[ERROR] Failed to retrieve input stream info\n");
        return -1;
    }
    avformat_alloc_output_context2(&(video->outputContext), NULL, NULL, outputFile);
    if (!video->outputContext) {
        printf("[ERROR] Failed to create output context\n");
        return -1;
    }
    printf("[OPEN] Video %s opened\n", filename);
    return 0;
}

int prepareStreamInfo(AVCodecContext** codecContext, AVCodec** codec, AVStream* stream) {
    *codec = avcodec_find_decoder(stream->codecpar->codec_id);
    if (!*codec) {
        printf("[ERROR] Failed to find input codec\n");
        return -1;
    }
    *codecContext = avcodec_alloc_context3(*codec);
    if (!codecContext) {
        printf("[ERROR] Failed to allocate memory for input codec context\n");
        return -1;
    }
    if (avcodec_parameters_to_context(*codecContext, stream->codecpar) < 0) {
        printf("[ERROR] Failed to fill input codec context\n");
        return -1;
    }
    if (avcodec_open2(*codecContext, *codec, NULL) < 0) {
        printf("[ERROR] Failed to open input codec\n");
        return -1;
    }
    return 0;
}

int findStreams(Video* video, char* filename, char* outputFile) {
    if (openVideo(video, filename, outputFile) < 0) {
        printf("[ERROR] Video %s failed to open\n", filename);
        return -1;
    }
    for (int i = 0; i < video->inputContext->nb_streams; i++) {
        video->inputStream = video->inputContext->streams[i];
        if (video->inputContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video->videoStream = i;
            if (prepareStreamInfo(&(video->videoCodecContext_I), &(video->videoCodec), video->inputStream) < 0) {
                printf("[ERROR] Could not prepare video stream information\n");
                return -1;video->outputStream->time_base = video->audioCodecContext_O->time_base;
            }
        } else if (video->inputContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            video->audioStream = i;
            if (prepareStreamInfo(&(video->audioCodecContext_I), &(video->audioCodec), video->inputStream) < 0) {
                printf("[ERROR] Could not prepare audio stream information\n");
                return -1;
            }
        }
        video->outputStream = avformat_new_stream(video->outputContext, NULL);
        if (!video->outputStream) {
            printf("[ERROR] Failed allocating output stream\n");
            return -1;
        }
        if (avcodec_parameters_copy(video->outputStream->codecpar, video->inputStream->codecpar) < 0) {
            printf("[ERROR] Failed to copy codec parameters\n");
            return -1;
        }
    }
    if (video->videoStream == -1) {
        printf("[ERROR] Video stream for %s not found\n", filename);
        return -1;
    }
    if (video->audioStream == -1) {
        printf("[ERROR] Audio stream for %s not found\n", filename);
        return -1;
    }
    if (!(video->outputContext->oformat->flags & AVFMT_NOFILE)) {
    if (avio_open(&(video->outputContext->pb), outputFile, AVIO_FLAG_WRITE) < 0) {
      printf("Could not open output file %s", outputFile);
      return -1;
    }
  }
    return 0;
}

int prepareAudioOutStream(Video* video) {
    video->audioCodec = avcodec_find_encoder_by_name("mp2");
    if (!video->audioCodec) {
        printf("[ERROR] Failed to find audio output codec\n");
        return -1;
    }
    video->audioCodecContext_O = avcodec_alloc_context3(video->audioCodec);
    if (!video->audioCodecContext_O) {
        printf("[ERROR] Failed to allocate memory for audio output codec context\n");
        return -1;
    }
    // Quite possibly the issue
    video->audioCodecContext_O->channels = video->audioCodecContext_I->channels;
    video->audioCodecContext_O->channel_layout = av_get_default_channel_layout(video->audioCodecContext_O->channels);
    video->audioCodecContext_O->sample_rate = video->audioCodecContext_I->sample_rate;
    video->audioCodecContext_O->sample_fmt = video->audioCodec->sample_fmts[0];
    video->audioCodecContext_O->bit_rate = video->audioCodecContext_I->bit_rate;
    video->audioCodecContext_O->time_base = video->audioCodecContext_I->time_base;
    video->audioCodecContext_O->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
    if (avcodec_open2(video->audioCodecContext_O, video->audioCodec, NULL) < 0) {
        printf("[ERROR] Failed to open audio output codec\n");
        return -1;
    }
    if (avcodec_parameters_from_context(getAudioStream(video)->codecpar, video->audioCodecContext_O) < 0) {
        printf("[ERROR] Failed to fill audio stream\n");
        return -1;
    }
    return 0;
}

int decodeAudio(Video* video, AVPacket* packet, AVFrame* frame) {
    int response = avcodec_send_packet(video->audioCodecContext_I, packet);
    if (response < 0) {
        printf("[ERROR] Failed to send audio packet to decoder\n");
        return response;
    }
    while (response >= 0) {
        response = avcodec_receive_frame(video->audioCodecContext_I, frame);
        if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
            break;
        } else if (response < 0) {
            printf("[ERROR] Failed to receive audio frame from decoder\n");
            return response;
        }
        if (response >= 0) {
            // Do stuff and encode
            if (encodeAudio(video, frame) < 0) {
                printf("[ERROR] Failed to encode new audio\n");
                return -1;
            }
        }
        av_frame_unref(frame);
    }
    return 0;
}

int encodeAudio(Video* video, AVFrame* frame) {
    AVPacket* packet = av_packet_alloc();
    if (!packet) {
        printf("[ERROR] Could not allocate memory for audio output packet\n");
        return -1;
    }
    int response = avcodec_send_frame(video->audioCodecContext_O, frame);
    if (response < 0) {
        printf("[ERROR] Failed to send audio frame for encoding\n");
        return response;
    }
    while (response >= 0) {
        response = avcodec_receive_packet(video->audioCodecContext_O, packet);
        if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
            break;
        } else if (response < 0) {
            printf("[ERROR] Failed to receive audio packet from encoder\n");
            return response;
        }
        packet->stream_index = video->audioStream;
        video->inputStream = getAudioStream(video);
        video->outputStream = video->outputContext->streams[packet->stream_index];
        packet->pts = av_rescale_q_rnd(packet->pts, video->inputStream->time_base, video->outputStream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
        packet->dts = av_rescale_q_rnd(packet->dts, video->inputStream->time_base, video->outputStream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
        packet->duration = av_rescale_q(packet->duration, video->inputStream->time_base, video->outputStream->time_base);
        packet->pos = -1;
        //av_packet_rescale_ts(packet, video->inputStream->time_base, video->outputStream->time_base);

        response = av_interleaved_write_frame(video->outputContext, packet);
        if (response < 0) {
            printf("[ERROR] Failed to write audio packet\n");
            break;
        }
    }
    av_packet_unref(packet);
    av_packet_free(&packet);
    return 0;
}

int readFrames(Video* video, AVPacket* packet, AVFrame* frame) {
    if (!packet) {
        printf("[ERROR] Packet not allocated to be read\n");
        return -1;
    }
    if (!frame) {
        printf("[ERROR] Frame not allocated to be read\n");
        return -1;
    }
    if (prepareVideoOutStream(video) < 0) {
        printf("[ERROR] Failed to prepare output video stream\n");
        return -1;
    }
    if (prepareAudioOutStream(video) < 0) {
        printf("[ERROR] Failed to prepare output audio stream\n");
        return -1;
    }
    int frameNum = 0;
    while (av_read_frame(video->inputContext, packet) >= 0) {
        printf("[READ] Reading frame %i\n", frameNum);
        if (packet->stream_index == video->videoStream) {
            if (decodeVideo(video, packet, frame) < 0) {
                printf("[ERROR] Failed to decode and encode video\n");
                return -1;
            }
        } else if (packet->stream_index == video->audioStream) {
            if (decodeAudio(video, packet, frame) < 0) {
                printf("[ERROR] Failed to decode and encode audio\n");
                return -1;
            }
        }
        av_packet_unref(packet);
        frameNum++;
    }
    // Flush encoder
    encodeVideo(video, NULL);
    encodeAudio(video, NULL);
    av_write_trailer(video->outputContext);
    return 0;
}

运行所有功能的主要方法:

int main(int argc, char* argv[]) {
    Video* video = (Video*)malloc(sizeof(Video));
    initVideo(video);
    if (findStreams(video, argv[1], argv[2]) < 0) {
        printf("[ERROR] Could not find streams\n");
        return -1;
    }

    AVDictionary* dic = NULL;
    if (avformat_write_header(video->outputContext, &dic) < 0) {
        printf("[ERROR] Error while writing header to output file\n");
        return -1;
    }
    AVFrame* frame = av_frame_alloc();
    AVPacket* packet = av_packet_alloc();
    if (readFrames(video, packet, frame) < 0) {
        printf("[ERROR] Failed to read and write new video\n");
        return -1;
    }
    freeVideo(video); // Frees all codecs and contexts and the video
    return 0;
}

我试图对代码进行布局,以便无需向上滚动即可从上至下读取代码。

我意识到在复制视频时,我可以通过AVPacket写入输出文件,但是我希望将来能够与AVFrame一起使用,因此我以这种方式编写了视频。我感觉我的音频表现方式出现问题是由于prepareAudioOutStream()函数的音频输出AVCodecContext。

事实证明,阅读FFmpeg文档对解决此问题以及其他在线资源都无济于事。我必须缺少某些东西(或不需要的东西),这样任何指向正确方向的东西都会有所帮助。

谢谢。

2 个答案:

答案 0 :(得分:2)

我是音频工程师,而不是编码员,但我希望这可能对您有所帮助。可能发生的情况是您的位深度被截断了;例如,将24位音频截断为16位,这会听起来失真且嘈杂。从最高有效位截断的每个位将削减6dB的净​​空。这将增加本底噪声,并随着显着减少的比特率增加,将大声但清晰的正弦波稳定地转变为失真的方波。

在重新编码过程中检查位深度选项。可能是您的编码器对其位深度有限制。检查源位深度和重新编码的位深度,看看有什么区别。您可以为此使用VLC媒体播放器。

还建议您在编码之前在信号中留出一些余量(至少0.1 dB。)预编码的音频可能已经被最大化,因此重新编码可能会增加一些轻微的失真。

更多信息在这里:

Reducing sample bit-depth by truncating

https://www.apple.com/itunes/docs/apple-digital-masters.pdf

答案 1 :(得分:1)

感谢Alexis(如上所述),我能够弄清楚问题出在哪里。我意识到我需要对音频进行重新采样,以使其在编码时匹配。

我基本上必须创建一个SwrContext并使用swr_initswr_convert_frame函数对音频进行重新采样。我在这里遵循的代码是:https://ffmpeg.org/doxygen/trunk/transcode_aac_8c-example.html

此外,请确保为重采样帧设置channel_layoutsample_rateformat;否则,您将无法转换框架。