admin管理员组

文章数量:1395185

I am working on an C++ application that captures audio using miniaudio, and sends the data to a RTMP server running on nginx which will generate an HLS stream to be consumed by a web browser. I was successful in encoding the data and writing to an .flv file (which I believe to be the container for RTMP), and everything works out fine.

I've also tested the server by running the ffmpeg cli tool directly, and the RTMP server is generating the .m3u8 playlist file as well as the .ts files correctly.

The problem I'm having is that when I change the output on avio_open2() to use the RTMP server url instead of a file name, the HLS files are not being generated, even though I get logging information about a successful connection, and also see "Audio Data" packets being sent to the server and the respective ACK response when using Wireshark.

Is there anything that is conceptually different between encoding to a file or to a server when using the ffmpeg libav?

I have a class for the audio encoding:

AudioEncoder(int sampleRate){
  av_log_set_level(AV_LOG_DEBUG);
  m_formatContext = avformat_alloc_context();
  const AVOutputFormat* outputFormat = av_guess_format("flv", NULL, NULL);
  if (!outputFormat) {
    std::cerr << "Could not find aac output format" << std::endl;
    exit(-1);
  }
  m_formatContext->oformat = outputFormat;

  result = avio_open2(&m_ioContext, "rtmp://localhost/live/test",AVIO_FLAG_WRITE, NULL, NULL);
  if( result < 0){
    std::cerr << "Could not open output stream: " << error_string(result) << std::endl;
    exit(-1); 
  }
  m_formatContext->pb = m_ioContext;
  
  const AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
  if(!codec){
    std::cerr << "Codec not found." << std::endl;
    exit(-1);
  }
  
  m_codecContext = avcodec_alloc_context3(codec);
  if (!m_codecContext) {
    std::cerr << "Could not alloc codec context" << std::endl;
    exit(-1);
  }
  AVChannelLayout layout;
  av_channel_layout_default(&layout, 1);
  m_codecContext->sample_fmt = AV_SAMPLE_FMT_FLTP;
  m_codecContext->bit_rate = 128000;
  m_codecContext->sample_rate = sampleRate;
  m_codecContext->ch_layout = layout;

  m_avStream = avformat_new_stream(m_formatContext, codec);
  if (!m_avStream) {
    std::cerr << "Could not create stream." << std::endl;
    exit(-1);
  } 
  result = avcodec_parameters_from_context(m_avStream->codecpar, m_codecContext);
  if ( result < 0) {
    std::cerr << "Failed to copy codec parameters to stream: " << error_string(result) << std::endl;
    exit(-1);
  }
  m_avStream->time_base = AVRational{1, m_codecContext->sample_rate};

  result = avcodec_open2(m_codecContext, codec, NULL);
  if ( result < 0) {
    std::cerr << "Failed to open codec: "<< error_string(result) << std::endl;
    exit(-1);
  }

  result = avformat_write_header(m_formatContext, NULL);
  if ( result < 0) {
    std::cerr << "Failed to write format header: "<< error_string(result) << std::endl;
    exit(-1);
  }
}

And an Encode function that is called by miniaudio:

void Encode(const void* data, unsigned int frames){
  AVPacket* packet = av_packet_alloc();
  if (!packet) {
    std::cerr << "Error allocating packet" << std::endl;
    return;
  }

  AVFrame* frame = av_frame_alloc();
  if (!frame) {
    std::cerr << "Error allocating frame" << std::endl;
    av_packet_free(&packet);
    return;
  }
  frame->format = m_codecContext->sample_fmt;
  frame->ch_layout = m_codecContext->ch_layout;
  frame->sample_rate = m_codecContext -> sample_rate;
  frame->nb_samples = frames;
  if (frames) {
    int result = av_frame_get_buffer(frame, 0);
    if ( result < 0) {
      std::cerr << "Error allocating frame buffer: " << error_string(result) << std::endl;
      av_frame_free(&frame);
      av_packet_free(&packet);
      return;
    }
  }
  frame->data[0] = (uint8_t*)data;

  int result = avcodec_send_frame(m_codecContext, frame); 
  if (result < 0) {
    std::cerr << "Error sending frame to encoder: " << error_string(result) << std::endl; 
  }

  result = avcodec_receive_packet(m_codecContext, packet); 
  if (result == AVERROR(EAGAIN) || result == AVERROR_EOF) {
    std::cout << "EAGAIN" << std::endl;
    // If we get AVERROR_EAGAIN, the encoder needs more data, so we'll return and try again later
    av_frame_free(&frame);
    av_packet_free(&packet);
    return;
  } else if (result < 0) {
    std::cerr << "Error receiving packet from encoder: " << error_string(result) << std::endl;
    av_frame_free(&frame);
    av_packet_free(&packet);
    return;
  }
  else {
    packet->stream_index = m_avStream->index;
    packet->pts = av_rescale_q(frames, AVRational{1, m_codecContext->sample_rate}, m_avStream->time_base);
    packet->dts = packet->pts;
    packet->duration = av_rescale_q(1024, AVRational{1, m_codecContext->sample_rate}, m_avStream->time_base);

    result = av_write_frame(m_formatContext, packet);
    if ( result < 0) {
      std::cerr << "Error writing frame: " << error_string(result) << std::endl;
    }
  }
  //Clean up resources
  av_frame_free(&frame);
  av_packet_unref(packet);
  av_packet_free(&packet);
}

On a final note, I have been able to stream to a TCP server developed in Golang using ADTS as the container and the same method, but I've decided to delegate the task to Nginx.

本文标签: cProblems encoding audio stream for RTMP server using ffmpeg librariesStack Overflow