#ffmpeg #sdl #sdl-2
#ffmpeg #sdl #sdl-2
Вопрос:
Я пытаюсь воспроизвести аудио из файла mp4 с использованием SDL2 и FFmpeg, и использование SDL_QueueAudio
кажется намного проще, чем настройка обратного вызова.
Все решения, которые я нашел, будь то здесь или в руководствах dranger, устарели или используют обратные вызовы. Я пытался просмотреть все вопросы как с тегами ffmpeg, так и sdl (их не так много), но безрезультатно. Я попытался преобразовать руководство dranger для использования не устаревших вызовов, но столкнулся с той же проблемой. Я использую C, FFmpeg 4.1 и SDL 2.0.9.
Это настройка для avcodecontext и AVCodec:
int audioStream = -1;
for (i = 0; i < formatContext->nb_streams; i ) {
if (audioStream < 0 amp;amp; formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
audioStream = i;
}
}
AVCodecParameters *audioParams = formatContext->streams[audioStream]->codecpar;
AVCodec *audioCodec = avcodec_find_decoder(audioParams->codec_id);
AVCodecContext *audioCodecCtx = avcodec_alloc_context3(NULL);
avcodec_open2(audioCodecCtx, audioCodec, NULL);
SDL_Init(SDL_INIT_AUDIO)
SDL_AudioSpec desired, obtained;
SDL_zero(desired);
SDL_zero(obtained);
desired.freq = audioCodecCtx->sample_rate;
desired.format = AUDIO_F32SYS;
desired.channels = audioCodecCtx->channels;
desired.silence = 0;
desired.samples = AUDIO_BUFFER_SIZE;
SDL_AudioDeviceID audioDevice = SDL_OpenAudioDevice(NULL, 0, amp;desired, amp;obtained, SDL_AUDIO_ALLOW_ANY_CHANGE);
Это основной цикл декодирования пакетов:
while (av_read_frame(formatContext, amp;packet) >= 0) {
if (packet.stream_index == audioStream) {
if (!avcodec_send_packet(audioCodecCtx, amp;packet)) {
avcodec_receive_frame(audioCodecCtx, audioFrame);
SDL_QueueAudio(audioDevice, audioFrame->data[0], audioFrame->linesize[0]);
}
}
}
Звук воспроизводится с правильной скоростью, но на гораздо более высоком уровне, чем он есть на самом деле. Я бы хотел, чтобы он звучал так же, как в любом медиаплеере.
Редактировать: Я только что понял, что в тестовом видео есть стереозвук, но я стою только в очереди audioFrame.data[0]
, что, как я полагаю, означает, что я играю только на одном канале. Я попробовал постановку в очередь, audioFrame.data[1]
в которой также есть данные, но это не решило проблему. Прав ли я, и если да, то как мне воспроизвести оба канала?
Комментарии:
1. Вы проверили значения в
obtained
структуре (в основном частоту)?2. Для моего тестового видео
obtained.freq
равно 44100, что совпадает с тем, что сообщает VLC.
Ответ №1:
Возможно, уже слишком поздно отвечать на этот вопрос, но я столкнулся с той же проблемой, и теперь я нашел решение, которое работает для меня, поэтому я публикую это.
Проблема здесь, вероятно, в том, что формат аудио, декодируемый FFmpeg, является форматом AV_SAMPLE_FMT_FLTP (float planer), где каналы хранятся отдельно, как frame->data[0]
и frame->data[1]
.
Нам нужно преобразовать его в формат, который объединяет эти каналы в один массив, используя swr_convert()
И вот мое решение.
- Настройка SwrContext
SwrContext *resampler = swr_alloc_set_opts(NULL,
audioCodecCtx->channel_layout,
AV_SAMPLE_FMT_S16,
44100,
audioCodecCtx->channel_layout,
audioCodecCtx->sample_fmt,
audioCodecCtx->sample_rate,
0,
NULL);
swr_init(resampler);
- Настройка звука SDL
SDL_AudioDeviceID dev;
SDL_AudioSpec want, have;
SDL_zero(want);
SDL_zero(have);
want.freq = 44100;
want.channels = audioCodecCtx->channels;
want.format = AUDIO_S16SYS;
dev = SDL_OpenAudioDevice(NULL, 0, amp;want, amp;have, 0);
SDL_PauseAudioDevice(dev, 0);
наконец, цикл декодирования
int ret = 0;
AVPacket *packet = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
AVFrame *audioframe = av_frame_alloc();
while (true){
ret = av_read_frame(formatContext, packet);
if (ret < 0) break;
AVStream *stream = formatContext->streams[packet->stream_index];
if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){
ret = avcodec_send_packet(audioCodecCtx, packet);
while (ret >= 0){
ret = avcodec_receive_frame(audioCodecCtx, frame);
if (ret >= 0){
int dst_samples = frame->channels * av_rescale_rnd(
swr_get_delay(resampler, frame->sample_rate)
frame->nb_samples,
44100,
frame->sample_rate,
AV_ROUND_UP);
uint8_t *audiobuf = NULL;
ret = av_samples_alloc(amp;audiobuf,
NULL,
1,
dst_samples,
AV_SAMPLE_FMT_S16,
1);
dst_samples = frame->channels * swr_convert(
resampler,
amp;audiobuf,
dst_samples,
(const uint8_t**) frame->data,
frame->nb_samples);
ret = av_samples_fill_arrays(audioframe->data,
audioframe->linesize,
audiobuf,
1,
dst_samples,
AV_SAMPLE_FMT_S16,
1);
SDL_QueueAudio(dev,
audioframe->data[0],
audioframe->linesize[0]);
}
}
}
}
Ответ №2:
Что сработало для меня, так это настройка частоты. Попробуйте изменить частоту на что-то вроде audioCodecCtx->sample_rate * 0.5
.