int OggOpusDataEncoder::OggopusEncode()

in nlsCppSdk/encoder/oggopusEncoder.cpp [329:557]


int OggOpusDataEncoder::OggopusEncode(const char *input_data, int length) {
  if (NULL == ogg_opus_para_) {
    return -(OggOpusInvalidState);
  }

  int ret = 0;
  char *tmp_buf = NULL;
  // short *tmp_2 = (short *)input_data;
  int tmp_length = 0;
  if (is_first_frame_processed_ && (length > 0)) {
    // the first frame has been processed
    tmp_length = length;
    tmp_buf = reinterpret_cast<char *>(malloc(tmp_length));
    // memset(tmp_buf, 0, tmp_length);
    memcpy(tmp_buf, input_data, tmp_length);
  } else {
    // 第一帧
    if (length == frame_sample_bytes_) {
      unsigned char header_data[276] = {0};
      // 将header结构写入header_data中
      int packet_size = OpusHeaderToPacket(&ogg_opus_para_->header, header_data,
                                           sizeof(header_data));
      ogg_opus_para_->op.packet = header_data;  // 填写ogg_packet
      ogg_opus_para_->op.bytes = packet_size;
      ogg_opus_para_->op.b_o_s = 1;
      ogg_opus_para_->op.e_o_s = 0;
      ogg_opus_para_->op.granulepos = 0;
      ogg_opus_para_->op.packetno = 0;
      // 原始数据要封装在ogg_packet中通过ogg_stream_packetin方法
      // 写入到ogg_stream_state
      ogg_stream_packetin(&ogg_opus_para_->os, &ogg_opus_para_->op);

      // os写入og(page的信息),直到写完
      while (
          (ret = ogg_stream_flush(&ogg_opus_para_->os, &ogg_opus_para_->og))) {
        if (!ret) break;
        ret = ogg_opus_para_->WritePage();  //把og的header和body送出去
        if (ret !=
            ogg_opus_para_->og.header_len + ogg_opus_para_->og.body_len) {
          LOG_ERROR("error: failed writing header to output stream");
          return -(OggOpusEncodeFailed);
        }
      }  // while

      // start()函数中已经填充了comments
      ogg_opus_para_->op.packet =
          (unsigned char *)ogg_opus_para_->ogg_encode_opt.comments;
      ogg_opus_para_->op.bytes = ogg_opus_para_->ogg_encode_opt.comments_length;
      ogg_opus_para_->op.b_o_s = 0;
      ogg_opus_para_->op.packetno = 1;
      // 原始数据要封装在ogg_packet中通过ogg_stream_packetin方法
      // 写入到ogg_stream_state
      ogg_stream_packetin(&ogg_opus_para_->os, &ogg_opus_para_->op);

      // os写入og(page的信息),直到写完
      while (
          (ret = ogg_stream_flush(&ogg_opus_para_->os, &ogg_opus_para_->og))) {
        if (!ret) break;
        ret = ogg_opus_para_->WritePage();  //把og的header和body送出去
        if (ret !=
            ogg_opus_para_->og.header_len + ogg_opus_para_->og.body_len) {
          LOG_ERROR("error: failed writing header to output stream");
          return -(OggOpusEncodeFailed);
        }
      }  // while

      tmp_length = length * 2;
      tmp_buf = reinterpret_cast<char *>(malloc(tmp_length));
      memset(tmp_buf, 0, tmp_length);
      memcpy(tmp_buf + length, input_data, length);  // 音频数据封入
      is_first_frame_processed_ = true;
    } else {  // input length is invalid
      ogg_opus_para_->op.e_o_s = 1;
    }
  }

  int size_segments, cur_frame_size;
  ogg_opus_para_->id++;

  if (ogg_opus_para_->nb_samples < 0) {
    ogg_opus_para_->nb_samples =
        ogg_opus_para_->ogg_encode_opt
            .read_func(/* WavRead() */
                       ogg_opus_para_->ogg_encode_opt.read_info,
                       ogg_opus_para_->input, frame_sample_num_,
                       /* 将tmp_buf(原始数据)Wav写入到 ogg_opus_para中 */
                       &tmp_buf, &tmp_length);
  }

  if (ogg_opus_para_->start_time == 0) {
    ogg_opus_para_->start_time = time(NULL);
  }

  cur_frame_size = frame_sample_num_;

  if (ogg_opus_para_->nb_samples < cur_frame_size) {
    /*Avoid making the final packet 20ms or more longer than needed.*/
    cur_frame_size -=
        ((cur_frame_size -
          (ogg_opus_para_->nb_samples >= 0 ? ogg_opus_para_->nb_samples : 1)) /
         320) *
        320;
    /*No fancy end padding, just fill with zeros for now.*/
    for (int i = ogg_opus_para_->nb_samples * channel_num_;
         i < cur_frame_size * channel_num_; i++) {
      ogg_opus_para_->input[i] = 0;
    }
  }

  if (cur_frame_size <= 0) {
    LOG_WARN("cur_frame_size = %d, nb_samples = %d", cur_frame_size,
             ogg_opus_para_->nb_samples);
    if (tmp_buf) {
      free(tmp_buf);
      tmp_buf = NULL;
    }
    return -(OggOpusEncodeFailed);
  }

  /* 成功,是被编码包的长度(字节数),失败,一个负的错误代码 */
  ogg_opus_para_->nbBytes =
      /* 根据浮点输入对一个 Opus帧进行编码. */
      opus_multistream_encode_float(ogg_opus_para_->opus_multistream_encoder,
                                    ogg_opus_para_->input, cur_frame_size,
                                    ogg_opus_para_->packet,
                                    ogg_opus_para_->max_frame_bytes);
  if (ogg_opus_para_->nbBytes < 0) {
    LOG_ERROR("encoding failed: %d %s. aborting ...", ogg_opus_para_->nbBytes,
              opus_strerror(ogg_opus_para_->nbBytes));
    LOG_INFO("cur_frame_size = %d", cur_frame_size);
    LOG_INFO("ogg_opus_para_->nbBytes = %d", ogg_opus_para_->nbBytes);
    if (tmp_buf) {
      free(tmp_buf);
      tmp_buf = NULL;
    }
    return -(OggOpusEncodeFailed);
  }
  ogg_opus_para_->enc_granulepos += FRAME_SAMPLE_NUM * 3;
  size_segments = (ogg_opus_para_->nbBytes + 255) / 255;

  /*Flush early if adding this packet would make us end up with a
    continued page which we wouldn't have otherwise.*/
  while ((((size_segments <= 255) &&
           (ogg_opus_para_->last_segments + size_segments > 255)) ||
          (ogg_opus_para_->enc_granulepos - ogg_opus_para_->last_granulepos >
           ogg_opus_para_->max_ogg_delay)) &&
         ogg_stream_flush_fill(&ogg_opus_para_->os, &ogg_opus_para_->og,
                               255 * 255)) {
    if (ogg_page_packets(&ogg_opus_para_->og) != 0) {
      ogg_opus_para_->last_granulepos =
          ogg_page_granulepos(&ogg_opus_para_->og);
    }
    ogg_opus_para_->last_segments -= ogg_opus_para_->og.header[26];
    ret = ogg_opus_para_->WritePage();
    if (ret != ogg_opus_para_->og.header_len + ogg_opus_para_->og.body_len) {
      LOG_ERROR("error: failed writing data to output stream");
      if (tmp_buf) {
        free(tmp_buf);
        tmp_buf = NULL;
      }
      return -(OggOpusEncodeFailed);
    }
  }

  if ((!ogg_opus_para_->op.e_o_s) && ogg_opus_para_->max_ogg_delay > 5760) {
    ogg_opus_para_->nb_samples =
        ogg_opus_para_->ogg_encode_opt
            .read_func(/* WavRead() */
                       ogg_opus_para_->ogg_encode_opt.read_info,
                       ogg_opus_para_->input, frame_sample_num_,
                       /* 将tmp_buf(原始数据)Wav写入到 ogg_opus_para中 */
                       &tmp_buf, &tmp_length);

    if (ogg_opus_para_->nb_samples == 0) {
      LOG_INFO("nb_samples = %d, max_ogg_delay = %d",
               ogg_opus_para_->nb_samples, ogg_opus_para_->max_ogg_delay);
      ogg_opus_para_->op.e_o_s = 1;
    }
  } else {
    ogg_opus_para_->nb_samples = -1;
  }

  ogg_opus_para_->op.packet = (unsigned char *)ogg_opus_para_->packet;
  ogg_opus_para_->op.bytes = ogg_opus_para_->nbBytes;
  ogg_opus_para_->op.b_o_s = 0;
  ogg_opus_para_->op.granulepos = ogg_opus_para_->enc_granulepos;
  if (ogg_opus_para_->op.e_o_s) {
    ogg_opus_para_->op.granulepos =
        ((ogg_opus_para_->original_sample_number * 48000 + sample_rate_ - 1) /
         sample_rate_) +
        ogg_opus_para_->header.preskip;
  }
  ogg_opus_para_->op.packetno = 2 + ogg_opus_para_->id;
  ogg_stream_packetin(&ogg_opus_para_->os, &ogg_opus_para_->op);
  ogg_opus_para_->last_segments += size_segments;

  /*If the stream is over or we're sure that the delayed flush will fire,
    go ahead and flush now to avoid adding delay.*/
  while ((ogg_opus_para_->op.e_o_s ||
          (ogg_opus_para_->enc_granulepos + (FRAME_SAMPLE_NUM * 3) -
               ogg_opus_para_->last_granulepos >
           ogg_opus_para_->max_ogg_delay) ||
          (ogg_opus_para_->last_segments >= 255))
             ? ogg_stream_flush_fill(&ogg_opus_para_->os, &ogg_opus_para_->og,
                                     255 * 255)
             : ogg_stream_pageout_fill(&ogg_opus_para_->os, &ogg_opus_para_->og,
                                       255 * 255)) {
    if (ogg_page_packets(&ogg_opus_para_->og) != 0) {
      ogg_opus_para_->last_granulepos =
          ogg_page_granulepos(&ogg_opus_para_->og);
    }
    ogg_opus_para_->last_segments -= ogg_opus_para_->og.header[26];
    ret = ogg_opus_para_->WritePage();
    if (ret != ogg_opus_para_->og.header_len + ogg_opus_para_->og.body_len) {
      LOG_ERROR("error: failed writing data to output stream");
      if (tmp_buf) {
        free(tmp_buf);
        tmp_buf = NULL;
      }
      return -(OggOpusEncodeFailed);
    }
  }
  if (tmp_buf) {
    free(tmp_buf);
    tmp_buf = NULL;
  }

  return Success;
}