c++ - 如何使用 UDP 通过网络正确发送 x264 编码帧?
问题描述
我正在尝试在获得它们时通过网络发送已编码的 h264 帧,目前我只是流式传输从每个帧获得的最终单元,因为它是编码的,这是正确的方法吗?
我在另一台计算机上编写了一个接收器应用程序来获取 nals 并将它们全部写入一个文件,当使用 vlc 播放时,我没有得到任何视频,而是听到了刺耳的噪音。我不确定问题出在哪里。我在创建的文件中包含了 FFmpeg -I 命令的结果。
编码器和发送者代码
//Udp initialisation
struct sockaddr_in broadcastAddr;
int sock;
int yes = 1;
int addr_len;
int count;
fd_set readfd;
char buffer[1024];
int i;
sock = socket(AF_INET, SOCK_DGRAM,0);
if(sock < 0){
std::cout << "Failed to initialise socket!" << std::endl;
}
int ret = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&yes, sizeof(yes));
if(ret < 0){
std::cout << "setsockopt error!" <<std::endl;
}
addr_len = sizeof(struct sockaddr_in); // the size of the address
memset((void*)&broadcastAddr,0,addr_len); //0 out the address bits
broadcastAddr.sin_family = AF_INET;
broadcastAddr.sin_addr.s_addr = INADDR_BROADCAST;
broadcastAddr.sin_port = PORT;
//Set the encoder parameters
x264_param_t param;
x264_param_default_preset(¶m,"veryfast","zerolatency");
param.i_threads = 1;
param.i_width = camera.getWidth();
param.i_height = camera.getHeight();
param.i_fps_num = 30;
param.i_fps_den = 1;
// Intra refres:
param.i_keyint_max = 30;
param.b_intra_refresh = 1;
//Rate control:
param.rc.i_rc_method = X264_RC_CRF;
param.rc.f_rf_constant = 25;
param.rc.f_rf_constant_max = 35;
//For streaming:
param.b_repeat_headers = 1;
param.b_annexb = 1;
x264_param_apply_profile(¶m, "baseline");
x264_t *encoder = x264_encoder_open(¶m); //H.264 encoder object
x264_picture_t pic_in, pic_out;
x264_picture_alloc(&pic_in, X264_CSP_I420,camera.getWidth(), camera.getHeight());
//Network abstraction layer units for broadcast
x264_nal_t *nals;
int i_nals;
while(true){
//If there is valid data in the processing queue
if(!encoderQueue.empty()){
//File the x264 input data structure with the file data
fillImage(encoderQueue.front(),camera.getWidth(),camera.getHeight(),&pic_in);
//Encode and send
int frame_size = x264_encoder_encode(encoder, &nals, &i_nals, &pic_in, &pic_out);
if (frame_size >= 0) {
//The frame is ready to be sent over UDP!
for(int i = 0; i < i_nals; i++){
ret = sendto(sock, &nals[0].p_payload, frame_size,0,(struct sockaddr*)&broadcastAddr,addr_len);
if(ret > 0){
std::cout << "Streamed frame nal unit " << i << std::endl;
} else{
std::cout << "Failed to stream nal unit " << i << std::endl;
}
}
}
else{
std::cout<<"Failed to encode h264 frame!" << std::endl;
}
//Finsihed with the current frame, pop it off the queue and remove any nals to do with it
encoderQueue.pop();
frame_size = 0;
nals = nullptr;
i_nals = 0;
}
}
接收器应用程序
#include <iostream>
#include <x264.h>
#include <Network/Network.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <queue>
#define BUFFER_LEN 10000
#define PORT_NO 3879
int main(int argc, const char * argv[]) {
FILE *file; //File to write the h264 nals too
//Declare the address memory space
struct sockaddr_in sockAddr , bcAddr;
socklen_t bcAddr_len = sizeof(&bcAddr); //Store the length of the broadcast address structure
//0 out the assigned memory
memset(&sockAddr, 0, sizeof(sockAddr));
memset(&bcAddr, 0 ,sizeof(bcAddr));
//Set the address parameters to look for incoming IpV4/UDP data
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(PORT_NO);
sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
bcAddr.sin_family = AF_INET;
bcAddr.sin_port = PORT_NO;
inet_aton("255.255.255.255",&bcAddr.sin_addr);
//Initialise a udp socket to read broadcast bytes
int soc = socket(AF_INET, SOCK_DGRAM,0);
//Check socket init
if(soc < 0){
std::cout << "Failed to initialise UDP socket!" << std::endl;
return -1;
}
//Bind the address details to the socket, check for errors
if(bind(soc, (struct sockaddr*)&sockAddr, sizeof(sockAddr)) < 0){
std::cout << "Failed to bind address structure to socket!" << std::endl;
return -2;
}
file = fopen("stream.h264","wb"); // Open the file for writing
unsigned char buffer[BUFFER_LEN];
while(true){
memset(&buffer, 0, sizeof(unsigned char) * BUFFER_LEN);
int recv_len = recvfrom(soc, buffer, BUFFER_LEN, 0, (struct sockaddr *)&bcAddr, &bcAddr_len);
std::cout<< "Received " << recv_len << "bytes on broadcast address" << std::endl;
fwrite(&buffer, sizeof(unsigned char), recv_len, file);
}
return 0;
}
FFMPEG -I 输出
任何帮助将不胜感激。
解决方案
没有“正确”或“不正确”的方法,只要接收方和发送方都同意协议。在这个例子中,VLC 正在寻找某种容器或格式。最常见的是传输流。
如果您打算读取 h264 流并将其写入磁盘,或者将其通过管道传输到期望附件 b 流的东西。你在做什么是正确的。如果您希望普通播放器直接读取流,请使用传输流。
推荐阅读
- keras - ImageData Generator 中的 validation_split 返回 0 张图像
- r - 从 R 问题中调用 Julia
- python - 将 n 个值插入一个空的双向链表(伪代码)
- node.js - 从我的 rest api 中提取数据到我的 VueJS 组件的数组数据属性中
- python - Python Facebook API v3.1
- python - Django:唯一的 order_reference
- laravel - 静态类方法的部分模拟不起作用
- javascript - 在表单提交上使用 javascript 捕获多个 HTML 元素值
- .net - 尝试在 VB.NET 或 C# 中匹配 Linux 摘要命令
- angular - 在我创建 mime-type.validator.ts 1 个作者的 1 个帖子后,应用程序无法编译