Как отправить данные нескольких чанков, используя nghttp2?

Я использую библиотеку nghttp2 для форматирования составных кадров для связи с Alexa. В настоящее время я могу получить ответ аудио с многокомпонентным сообщением. Но в настоящее время можно отправлять данные только до 16 КБ, я хочу выполнять потоковую передачу моих записанных данных, которая может быть больше 16 КБ в целом.

Может кто-нибудь, пожалуйста, помогите мне в отправке аудиоданных в чанке в AVS с помощью nghttp2?

Ждем ответа, помогите пожалуйста.

Благодарю.

Кроме того, я добавляю справочные функции из кода. Пожалуйста, игнорируйте соглашение об именах и другую логику, так как я просто стараюсь посылать данные размером более 16 КБ.

    //Callback function for the data to be send to the server
ssize_t data_prd_read_callback_1(nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length,uint32_t *data_flags, nghttp2_data_source *source, void *user_data)
{
    //uint8_t send_data[8000]; 
    char send_data[] = "\r\n\r\n--_____FINISH_HERE__________\r\nContent-Disposition: form-data; name=\"metadata\"\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n{\"context\":[{\"header\": {\"namespace\": \"SpeechSynthesizer\",\"name\": \"SpeechState\"},\"payload\": {\"token\":\"\",\"offsetInMilliseconds\":0,\"playerActivity\":\"IDLE\"}}],\"event\":{\"header\":{\"namespace\":\"SpeechRecognizer\",\"name\":\"Recognize\",\"messageId\":\"MID-123456\",\"dialogRequestId\":\"DRID-123456\"},\"payload\":{\"profile\":\"CLOSE_TALK\",\"format\":\"AUDIO_L16_RATE_16000_CHANNELS_1\"}}}\r\n\r\n--_____FINISH_HERE__________\r\nContent-Disposition: form-data; name=\"audio\"\r\nContent-Type: application/octet-stream\r\n\r\n";

    int fd = source->fd;
    uint8_t *audio_data;
    uint8_t *final_data_end, *temp_data;
    audio_data = malloc(AUDIO_FILE_SIZE);
    memset(audio_data, 0, AUDIO_FILE_SIZE);
    final_data_end = malloc(MAX_SIZE_TO_SEND);
    temp_data = final_data_end;
    memset(final_data_end, 0, MAX_SIZE_TO_SEND);
    int r;
    r = read(fd, audio_data, AUDIO_FILE_SIZE);
    if ( r == -1)
    {
        printf("JOSHI error while reading audio file\n");
    }
    int len, i;
    len = strlen(send_data);
    memcpy(final_data_end, send_data, strlen(send_data));
    final_data_end += strlen(send_data);
    memcpy(final_data_end, audio_data, AUDIO_FILE_SIZE);
    final_data_end += AUDIO_FILE_SIZE;
    memcpy(final_data_end, "\r\n--_____FINISH_HERE__________--", 32);
    memcpy(buf, temp_data, (strlen(send_data) + AUDIO_FILE_SIZE + 32));
    return (strlen(send_data) + AUDIO_FILE_SIZE + 32);
}


static int send_request(struct connection *conn, struct request *req) {
    nghttp2_nv nva[] = { 
                MAKE_NV_LL(":method", "POST"),
                MAKE_NV_L(":scheme", "https"),
                MAKE_NV_LL(":path", "/v20160207/events" ),
        MAKE_NV_LL("authorization", "Bearer "ACCESS_TOKEN""),
        MAKE_NV_LL("content-type", "multipart/form-data; boundary=_____FINISH_HERE__________")};
    int rv;

    nghttp2_data_provider data_prd;
    int file_descriptor;
    file_descriptor = open ("./audio.raw", O_RDONLY);
    if (file_descriptor == -1)
    {
        printf("error while reading the audio file\n");
    }
    data_prd.source.fd = file_descriptor;   // set the file descriptor 
    data_prd.read_callback = data_prd_read_callback_1;

    rv = nghttp2_submit_request(conn->ngh2, NULL, nva, ARRLEN(nva), &data_prd, req);
    temp_stream_id = rv;
    if (rv < 0) {
        fprintf(stderr, "In second error: (nghttp2_submit_requset) %s\n",nghttp2_strerror(rv));
        return -1; 
    }   
    return 0;
}

Я использую "nghttp2_session_send" для отправки с функцией send_callback. Всякий раз, когда я пытаюсь использовать файл, размер которого превышает 16 КБ, AVS будет сообщать с ОШИБКОЙ "нет многочастных данных со статусом:400". Если все данные меньше 16 КБ, то alexa ответит звуковыми данными, прикрепленными в ответ.

0 ответов

Вам нужно указать nghttp, что вы не закончили писать. Как написано в учебнике:

static ssize_t file_read_callback(nghttp2_session *session _U_,
                                  int32_t stream_id _U_, uint8_t *buf,
                                  size_t length, uint32_t *data_flags,
                                  nghttp2_data_source *source,
                                  void *user_data _U_) {
  int fd = source->fd;
  ssize_t r;
  while ((r = read(fd, buf, length)) == -1 && errno == EINTR)
    ;
  if (r == -1) {
    return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
  }
  if (r == 0) {
    *data_flags |= NGHTTP2_DATA_FLAG_EOF;
  }
  return r;
}

Вы можете видеть, что они устанавливают *data_flags равным NGHTTP2_DATA_FLAG_EOF, когда больше нет данных для чтения. Итак, если у вас есть несколько файлов МБ и только буфер 16 КБ, вам нужно сохранить некоторое смещение и при каждом вызове вашего data_prd_read_callback_1 записать максимум 16 КБ, а затем на последней итерации записать остальные (скажем, 10 КБ или что-то еще). Я использую это так:

ssize_t Message::Http2DataLoader( nghttp2_session *sess, int32_t sId, uint8_t *buf, size_t bufLen, uint32_t *dataFlags, nghttp2_data_source *src, void *hint )
{
   ssize_t ret = 0;
   Message* theRes = reinterpret_cast< Message* >( src->ptr );

   if ( bufLen <= theRes->getDataLen() - theRes->getWritten() )
   {
      std::memcpy( buf, theRes->getData() + theRes->getWritten(), bufLen );
      ret = bufLen;
   }
   else
   {
      std::memcpy( buf, theRes->getData() + theRes->getWritten(), theRes->getDataLen() - theRes->getWritten() );
      ret = theRes->getDataLen() - theRes->getWritten();
   }

   theRes->addWritten( ret );

   if( theRes->AllDataConsumed() )
   {
      *dataFlags |= NGHTTP2_DATA_FLAG_EOF;
   }

   return ret;
}

Класс Message - это контейнер, который может содержать либо данные тела, либо составные части, все составные части при добавлении записывают свои данные во внутренний буфер сообщения, а затем в обратном вызове загрузчика данных я просто записываю его в блоках по 16 КБ (или, лучше сказать, в блоках bufLen). Я отслеживаю, где я нахожусь в буфере, используя методы addWritten и getWritten. Конкурс проводится AllDataConsumed. Если вы читаете аудио из файла multipart, вам понадобится что-то вроде file_read_callback из учебника. Было бы хорошо, если бы вы инкапсулировали это в какое-нибудь сообщение класс, в котором вы можете добавить несколько частей json и multipart аудио и изменить обратный вызов dataLoader, чтобы сначала записать часть json, а затем начать чтение аудиофайла и добавление записи его в buf кусками по 16k(bufLen).

Nghttp2_session_send будет вызывать data_prd_read_callback_1, пока вы не напишете все ваши данные или какая - то ошибка происходит. Это означает, что вы можете обрабатывать запись JSON в буфер и запись аудиофайла в буфер по мере необходимости. Если все пойдет хорошо, он будет просто вызывать ваш обратный вызов, пока вы не установите *data_flags на NGHTTP2_DATA_FLAG_EOF.

Надеюсь, это тебе поможет.

Другие вопросы по тегам