c++ - libav 实时转码为 SFML SoundStream,乱码和噪音

标签 c++ ffmpeg sfml libav

我已经很接近了,但是使用输出示例格式或编解码器上下文似乎无法解决,也不知道从哪里开始。

#include <iostream>
#include <SFML/Audio.hpp>
#include "MyAudioStream.h"

extern "C"
{
#include <libavutil/opt.h>
#include <libavutil/avutil.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/audio_fifo.h>
#include <libswresample/swresample.h>
}

void setupInput(AVFormatContext *input_format_context, AVCodecContext **input_codec_context, const char *streamURL)
{
  // av_find_input_format("mp3");
  avformat_open_input(&input_format_context, streamURL, NULL, NULL);
  avformat_find_stream_info(input_format_context, NULL);

  AVDictionary *metadata = input_format_context->metadata;
  AVDictionaryEntry *name = av_dict_get(metadata, "icy-name", NULL, 0);
  if (name != NULL)
  {
    std::cout << name->value << std::endl;
  }
  AVDictionaryEntry *title = av_dict_get(metadata, "StreamTitle", NULL, 0);
  if (title != NULL)
  {
    std::cout << title->value << std::endl;
  }

  AVStream *stream = input_format_context->streams[0];
  AVCodecParameters *codec_params = stream->codecpar;
  AVCodec *codec = avcodec_find_decoder(codec_params->codec_id);
  *input_codec_context = avcodec_alloc_context3(codec);

  avcodec_parameters_to_context(*input_codec_context, codec_params);
  avcodec_open2(*input_codec_context, codec, NULL);
}

void setupOutput(AVCodecContext *input_codec_context, AVCodecContext **output_codec_context)
{
  AVCodec *output_codec = avcodec_find_encoder(AV_CODEC_ID_PCM_S16LE); // AV_CODEC_ID_PCM_S16LE ?? AV_CODEC_ID_PCM_S16BE
  *output_codec_context = avcodec_alloc_context3(output_codec);
  (*output_codec_context)->channels = 2;
  (*output_codec_context)->channel_layout = av_get_default_channel_layout(2);
  (*output_codec_context)->sample_rate = input_codec_context->sample_rate;
  (*output_codec_context)->sample_fmt = output_codec->sample_fmts[0]; // AV_SAMPLE_FMT_S16 ??
  avcodec_open2(*output_codec_context, output_codec, NULL);
}

void setupResampler(AVCodecContext *input_codec_context, AVCodecContext *output_codec_context, SwrContext **resample_context)
{
  *resample_context = swr_alloc_set_opts(
      *resample_context,
      output_codec_context->channel_layout,
      output_codec_context->sample_fmt,
      output_codec_context->sample_rate,
      input_codec_context->channel_layout,
      input_codec_context->sample_fmt,
      input_codec_context->sample_rate,
      0, NULL);
  swr_init(*resample_context);
}

MyAudioStream::MyAudioStream()
{
  input_format_context = avformat_alloc_context();
  resample_context = swr_alloc();
}

MyAudioStream::~MyAudioStream()
{
  // clean up
  avformat_close_input(&input_format_context);
  avformat_free_context(input_format_context);
}

void MyAudioStream::load(const char *streamURL)
{

  setupInput(input_format_context, &input_codec_context, streamURL);
  setupOutput(input_codec_context, &output_codec_context);
  setupResampler(input_codec_context, output_codec_context, &resample_context);

  initialize(output_codec_context->channels, output_codec_context->sample_rate);
}

bool MyAudioStream::onGetData(Chunk &data)
{

  // init
  AVFrame *input_frame = av_frame_alloc();
  AVPacket *input_packet = av_packet_alloc();
  input_packet->data = NULL;
  input_packet->size = 0;

  // read
  av_read_frame(input_format_context, input_packet);
  avcodec_send_packet(input_codec_context, input_packet);
  avcodec_receive_frame(input_codec_context, input_frame);

  // convert
  uint8_t *converted_input_samples = (uint8_t *)calloc(output_codec_context->channels, sizeof(*converted_input_samples));
  av_samples_alloc(&converted_input_samples, NULL, output_codec_context->channels, input_frame->nb_samples, output_codec_context->sample_fmt, 0);
  swr_convert(resample_context, &converted_input_samples, input_frame->nb_samples, (const uint8_t **)input_frame->extended_data, input_frame->nb_samples);

  data.sampleCount = input_frame->nb_samples;
  data.samples = (sf::Int16 *)converted_input_samples;

  // av_freep(&converted_input_samples[0]);
  // free(converted_input_samples);
  av_packet_free(&input_packet);
  av_frame_free(&input_frame);

  return true;
}

void MyAudioStream::onSeek(sf::Time timeOffset)
{
  // no op
}

sf::Int64 MyAudioStream::onLoop()
{
  // no loop
  return -1;
}

调用
#include <iostream>

#include "./MyAudioStream.h"

extern "C"
{
#include <libavutil/opt.h>
#include <libavutil/avutil.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}

const char *streamURL = "http://s5radio.ponyvillelive.com:8026/stream.mp3";

int main(int, char **)
{

  MyAudioStream myStream;

  myStream.load(streamURL);

  std::cout << "Hello, world!" << std::endl;

  myStream.play();

  while (myStream.getStatus() == MyAudioStream::Playing)
  {
    sf::sleep(sf::seconds(0.1f));
  }

  return 0;
}

最佳答案

我解决了。 swr_get_out_samples 返回的计数似乎是每个 channel 。设置sf::SoundStream::Chunk::sampleCount时我乘以2 ,如:

data.sampleCount = out_samples * 2;


那行得通。

关于c++ - libav 实时转码为 SFML SoundStream,乱码和噪音,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68043944/

相关文章:

ffmpeg 奇怪的 x264 编码行为

c++ - QVideoFrame 的许多构造函数中的 1 个的未定义引用

c++ - 从文件中加载 const 命名空间变量值

c# - SFML 3D 鼠标外观

C++ 错误 3 错误 C2159 : more than one storage class specified

java - 无法正确地将 std::pair 暴露给 java 绑定(bind)?

c++ - 如果重新定义一个内联函数呢?

video - ffmpeg 没有将一些 mov 视频转换为 mp4

c++ - Qt 从资源包目录中读取文件

c++ - SFML 2.1 网络