Tôi đang cố lấy mẫu lại các khung âm thanh đã giải mã từ 48KHz đến 44,1KHz bằng API libswresample. Mã của tôi là như sau:
// 'frame' là frame âm thanh được giải mã gốc
AVFrame *output_frame = av_frame_alloc();
// Không có cái này thì sẽ không có âm thanh nào ở đầu ra (tôi đoán là thứ PTS)
av_frame_copy_props(output_frame, frame);
đầu ra_frame->channel_layout = audioStream->codec->channel_layout;
out_frame->sample_rate = audioStream->codec->sample_rate;
out_frame->format = audioStream->codec->sample_fmt;
SwrContext *swr;
// Định cấu hình bối cảnh lấy mẫu lại
swr = swr_alloc_set_opts(NULL, // chúng tôi đang phân bổ một bối cảnh mới
AV_CH_LAYOUT_STEREO, // out_ch_layout
AV_SAMPLE_FMT_FLTP, // out_sample_fmt
44100, // out_sample_rate
AV_CH_LAYOUT_STEREO, // in_ch_layout
AV_SAMPLE_FMT_FLTP, // in_sample_fmt
48000, // in_sample_rate
0, // log_offset
KHÔNG); // log_ctx
// Khởi tạo bối cảnh lấy mẫu lại
swr_init(swr);
// Thực hiện chuyển đổi
swr_convert_frame(swr, out_frame, frame);
// Đóng bối cảnh lấy mẫu lại
swr_close(swr);
swr_free(&swr);
// Giải phóng khung ban đầu và thay thế bằng khung mới
av_frame_unref(khung);
trả về out_frame;
Với mã này, tôi có thể nghe thấy âm thanh ở đầu ra nhưng nó cũng ồn. Từ những gì tôi đã đọc, mã này không có av_frame_copy_props() là đủ, nhưng vì lý do nào đó nó không hoạt động. Có ý tưởng gì không?
biên tập:Luồng đầu vào mã hóa âm thanh bằng AAC, với số lượng mẫu là 1024. Tuy nhiên, sau khi chuyển đổi, số lượng mẫu là 925.
biên tập: Tôi cố gắng làm điều đó theo cách khác. Vì ứng dụng của tôi nhận được luồng từ bất kỳ nguồn nào nên một số luồng âm thanh là 48KHz và một số luồng khác là 44,1KHz. Vì vậy, tôi đã thử lấy mẫu lại từ 44.1 đến 48 để tránh mất mẫu. Nhưng hiện tại có hơn 1024 mẫu trên mỗi khung hình và quá trình mã hóa không thành công.
biên tập:Tôi đã thử sử dụng libavfilter thay vì chuỗi bộ lọc sau:
int init_filter_graph(AVStream *audio_st) {
// tạo biểu đồ mới
filter_graph = avfilter_graph_alloc();
nếu (!filter_graph) {
av_log(NULL, AV_LOG_ERROR, "không thể tạo biểu đồ lọc: hết bộ nhớ\n");
return -1;
}
AVFilter *abuffer = avfilter_get_by_name("abuffer");
AVFilter *aformat = avfilter_get_by_name("aformat");
AVFilter *asetnsamples = avfilter_get_by_name("asetnsamples");
AVFilter *abuffersink = avfilter_get_by_name("abuffersink");
int lỗi;
// tạo bộ lọc đệm
AVCodecContext *avctx = audio_st->codec;
AVRational time_base = audio_st->time_base;
snprintf(strbuf, sizeof(strbuf),
"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%" PRIx64,
time_base.num, time_base.den, avctx->sample_rate,
av_get_sample_fmt_name(avctx->sample_fmt),
avctx->channel_layout);
fprintf(stderr, "buffer: %s\n", strbuf);
err = avfilter_graph_create_filter(&abuffer_ctx, abuffer,
NULL, strbuf, NULL, filter_graph);
nếu (lỗi < 0) {
av_log(NULL, AV_LOG_ERROR, "lỗi khởi tạo bộ lọc bộ đệm\n");
trả lại lỗi;
}
// tạo bộ lọc định dạng
snprintf(strbuf, sizeof(strbuf),
"sample_fmts=%s:sample_rates=%d:channel_layouts=0x%" PRIx64,
av_get_sample_fmt_name(AV_SAMPLE_FMT_FLTP), 44100,
AV_CH_LAYOUT_STEREO);
fprintf(stderr, "aformat: %s\n", strbuf);
err = avfilter_graph_create_filter(&aformat_ctx, aformat,
NULL, strbuf, NULL, filter_graph);
nếu (lỗi < 0) {
av_log(NULL, AV_LOG_ERROR, "không thể tạo bộ lọc định dạng\n");
trả lại lỗi;
}
// tạo bộ lọc asetnsamples
snprintf(strbuf, sizeof(strbuf),
"n=1024:p=0");
fprintf(stderr, "asetnsamples: %s\n", strbuf);
err = avfilter_graph_create_filter(&asetnsamples_ctx, asetnsamples,
NULL, strbuf, NULL, filter_graph);
nếu (lỗi < 0) {
av_log(NULL, AV_LOG_ERROR, "không thể tạo bộ lọc mẫu tài sản\n");
trả lại lỗi;
}
// tạo bộ lọc đệm
err = avfilter_graph_create_filter(&abuffersink_ctx, abuffersink,
NULL, NULL, NULL, filter_graph);
nếu (lỗi < 0) {
av_log(NULL, AV_LOG_ERROR, "không thể tạo bộ lọc định dạng\n");
trả lại lỗi;
}
// kết nối đầu vào và đầu ra
if (err >= 0) err = avfilter_link(abuffer_ctx, 0, aformat_ctx, 0);
if (err >= 0) err = avfilter_link(aformat_ctx, 0, asetnsamples_ctx, 0);
if (err >= 0) err = avfilter_link(asetnsamples_ctx, 0, abuffersink_ctx, 0);
nếu (lỗi < 0) {
av_log(NULL, AV_LOG_ERROR, "lỗi kết nối bộ lọc\n");
trả lại lỗi;
}
lỗi = avfilter_graph_config(filter_graph, NULL);
nếu (lỗi < 0) {
av_log(NULL, AV_LOG_ERROR, "lỗi khi định cấu hình biểu đồ bộ lọc\n");
trả lại lỗi;
}
return 0;
}
Khung kết quả hiện có 1024 mẫu nhưng âm thanh vẫn bị giật.
Cuối cùng, tôi sử dụng đây Giải pháp giải quyết vấn đề này.
Đây là mã để tạo bộ lọc cho thiết lập của tôi (được lấy mẫu lại thành 44,1KHz)
AVFilterGraph *filter_graph = NULL;
AVFilterContext *buffersrc_ctx = NULL;
AVFilterContext *buffersink_ctx = NULL;
QString filter_description = "aresample=44100,aformat=sample_fmts=fltp:channel_layouts=stereo,asetnsamples=n=1024:p=0";
/**
* Khởi tạo bộ lọc chuyển đổi */
int khởi tạo_audio_filter(AVStream *inputStream) {
char args[512];
int ret;
AVFilter *buffersrc = avfilter_get_by_name("abuffer");
AVFilter *buffersink = avfilter_get_by_name("abuffersink");
AVFilterInOut *đầu ra = avfilter_inout_alloc();
AVFilterInOut *inputs = avfilter_inout_alloc();
filter_graph = avfilter_graph_alloc();
const enum AVSampleFormat out_sample_fmts[] = {AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE};
const int64_t out_channel_layouts[] = {AV_CH_LAYOUT_STEREO, -1};
const int out_sample_rates[] = {44100, -1};
snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%" PRIx64,
inputStream->codec->time_base.num, inputStream->codec->time_base.den,
inputStream->codec->sample_rate,
av_get_sample_fmt_name(inputStream->codec->sample_fmt),
inputStream->codec->channel_layout);
ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", args, NULL, filter_graph);
nếu (ret < 0) {
svsCritical("", QString("Không thể tạo biểu đồ lọc, lỗi: %1").arg(svsAvErrorToFormattedString(ret)))
return -1;
}
ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", NULL, NULL, filter_graph);
nếu (ret < 0) {
svsCritical("", QString("Không thể tạo vùng đệm chìm, lỗi: %1").arg(svsAvErrorToFormattedString(ret)))
return ret;
}
ret = av_opt_set_int_list(buffersink_ctx, "sample_fmts", out_sample_fmts, -1,
AV_OPT_SEARCH_CHILDREN);
nếu (ret < 0) {
svsCritical("", QString("Không thể đặt định dạng mẫu đầu ra, lỗi: %1").arg(svsAvErrorToFormattedString(ret)))
return ret;
}
ret = av_opt_set_int_list(buffersink_ctx, "channel_layouts", out_channel_layouts, -1,
AV_OPT_SEARCH_CHILDREN);
nếu (ret < 0) {
svsCritical("", QString("Không thể thiết lập bố cục kênh đầu ra, lỗi: %1").arg(svsAvErrorToFormattedString(ret)))
return ret;
}
ret = av_opt_set_int_list(buffersink_ctx, "sample_rates", out_sample_rates, -1,
AV_OPT_SEARCH_CHILDREN);
nếu (ret < 0) {
svsCritical("", QString("Không thể đặt tốc độ lấy mẫu đầu ra, lỗi: %1").arg(svsAvErrorToFormattedString(ret)))
return ret;
}
/* Điểm cuối của biểu đồ lọc */
kết quả đầu ra -> name = av_strdup("in");
đầu ra -> filter_ctx = buffersrc_ctx;
kết quả đầu ra -> pad_idx = 0;
kết quả đầu ra -> tiếp theo = NULL;
/* Điểm cuối của biểu đồ lọc */
đầu vào -> name = av_strdup("out");
đầu vào -> filter_ctx = buffersink_ctx;
đầu vào -> pad_idx = 0;
đầu vào -> tiếp theo = NULL;
if ((ret = avfilter_graph_parse_ptr(filter_graph, filter_description.toStdString().c_str(), &inputs, &outputs, NULL)) < 0) {
svsCritical("", QString("Không thể thêm bộ lọc vào biểu đồ, lỗi: %1").arg(svsAvErrorToFormattedString(ret)))
}
if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0) {
svsCritical("", QString("Không thể định cấu hình biểu đồ, lỗi: %1").arg(svsAvErrorToFormattedString(ret)))
}
/* In bản tóm tắt của bộ đệm chìm
* Lưu ý: bộ đệm args được sử dụng lại để lưu trữ chuỗi bố cục kênh */
AVFilterLink *outlink = buffersink_ctx->inputs[0];
av_get_channel_layout_string(args, sizeof(args), -1, outlink->channel_layout);
svsInfo("", QString::asprintf("Đầu ra: srate:%dHz fmt:%s chlayout:%s\n",
(int) liên kết ngoài->sample_rate,
(char *) av_x_if_null(av_get_sample_fmt_name((AVSampleFormat) outlink->format), "?"),
tranh luận))
return 0;
}
và sử dụng bộ lọc:
AVFrame* resampleAudio(const QString& key, AVFrame *frame) {
/* Đẩy khung đã giải mã vào sơ đồ lọc */
qint32 ret;
ret = av_buffersrc_add_frame_flags(buffersrc_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF);
if(ret < 0) {
svsWarning(key, QString("Lỗi khi thêm khung vào bộ đệm: %1").arg(svsAvErrorToFormattedString(ret)))
// Xóa khung đầu vào và trả về null
av_frame_unref(khung);
return nullptr;
}
AVFrame *reampled_frame = av_frame_alloc();
/* Kéo các khung đã lọc từ sơ đồ lọc */
ret = av_buffersink_get_frame(buffersink_ctx, resampled_frame);
/* Đặt dấu thời gian trên khung được lấy mẫu lại */
resampled_frame->best_effort_timestamp = resampled_frame->pts;
if(ret < 0) {
// Điều này rất phổ biến. Đối với 48KHz -> 44,1KHz đối với một số khung hình đầu vào.
// bộ lọc không đủ dữ liệu để tạo bộ lọc khác.
av_frame_unref(khung);
av_frame_unref(resampled_frame);
return nullptr;
}
av_frame_unref(khung);
trả về resampled_frame;
}
Đặt trên các khung được lấy mẫu lại nỗ lực_tốt nhất_timestamp
Điều quan trọng là làm cho nó hoạt động tốt. Nhưng PTS của khung này được đặt theo bộ lọc.
Tôi là một lập trình viên xuất sắc, rất giỏi!