gpt4 book ai didi

c - FFmpeg - 音频编码在音频上产生额外的噪音

转载 作者:行者123 更新时间:2023-12-02 22:48:19 26 4
gpt4 key购买 nike

我正在尝试使用 FFmpeg 拍摄视频(在本例中为 MP4)并将其复制为另一个 MP4。这样我就可以掌握解码/编码视频的窍门,并在该过程中继续做其他事情。我的代码基本上获取一个视频文件,对视频和音频流进行解码,然后将视频和音频流编码为输出视频文件。

截至目前,我的代码仅适用于输入文件的视频流。输出文件的视频部分与输入文件的视频部分完全相同。但是,音频部分不是。输出的音频部分包含原始音频,但上面有噪音。可以把它想象成有人对着他们的麦克风尖叫,或者当音频声音太大以至于扬声器无法处理时。

我处理视频和音频流的解码/编码过程的方式是相同的,除了 AVCodecContext 设置不同(视频 --> frame_rate、width、height 等;音频 --> sample_rate 、 channel 等)。

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

视频结构:

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 文档和其他在线资源对解决此问题帮助不大。我一定是遗漏了一些东西(或者有一些不需要的东西),所以任何能给我指明正确方向的东西都会有所帮助。

谢谢。

最佳答案

我是音频工程师,不是编码员,但我希望这对您有所帮助。可能发生的情况是您的位深度被截断了;例如,24 位音频被截断为 16 位,这将听起来失真和嘈杂。 最重要截断的每一位将削减 6dB 的余量。这将增加本底噪声,并将响亮但清晰的正弦波稳定地变成失真的方波,因为显着的比特减少增加了。

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

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

更多信息在这里:

Reducing sample bit-depth by truncating

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

关于c - FFmpeg - 音频编码在音频上产生额外的噪音,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61846094/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com