我使用nghttp2库来格式化多个部分的帧,以便与Alexa通信。我目前可以通过一条分片的消息得到音频的响应。但是,目前只能发送16KB的数据,我想做我的记录数据的流式传输,可以大于16KB作为一个整体。
有没有人可以帮我用nghttp2把分块的音频数据发送到录像机?
期待您的回复,请多多帮助。
谢谢。
此外,我还添加了代码中的引用函数。请忽略命名约定和其他逻辑,因为我只是粗略地尝试发送超过16KB大小的数据。
//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;
}我使用send_callback函数发送时使用了"nghttp2_session_send“。每当我尝试处理超过16KB大小的文件时,AVS都会回复错误"there no multipart data with status:400“。如果整个数据小于16KB,alexa将回复附加的音频数据作为响应。
发布于 2019-10-03 21:24:58
你需要向nghttp说明你还没有写完。在tutorial中编写
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。因此,如果你有几个MB的文件,只有16k的缓冲区,那么你必须保留一些偏移量,在你的data_prd_read_callback_1的每次调用中,写入最大值16k,然后在最后一次迭代中写入其余的(比如10k或其他任何剩余的)。我这样使用它:
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类是一个容器,它可以包含正文数据,也可以包含多个部分,所有的多部分在添加时将其数据写入内部消息缓冲区,然后在dataloader回调中,我只需将其写入16k块(或者更好地说,在bufLen块中)。我使用addWritten和getWritten方法跟踪我在缓冲区中的位置。比赛由AllDataConsumed.负责如果你正在从文件中读取音频多部分,那么你需要像教程中的file_read_callback这样的东西。如果您将其封装到某个消息类中,您可以在其中添加一些json多部分和音频多部分,并修改dataLoader回调以首先写入json部分,然后开始读取音频文件并以16k(bufLen)块的形式将其写入buf,这将是很好的。
nghttp2_session_send将调用您的data_prd_read_callback_1,直到您写入所有数据或发生错误。这意味着您可以根据需要处理JSON写入缓冲区和音频文件写入缓冲区。如果一切顺利,它将一直调用您的回调,直到您将*data_flags设置为NGHTTP2_DATA_FLAG_EOF。
希望这对你有帮助。
https://stackoverflow.com/questions/45633787
复制相似问题