linux fifo数据流,在linux / bash中使用非阻塞FIFO流式传输视频(示例代码)

我正在努力实现以下目标:

将我的Raspberry Pi相机中的视频写入磁盘,不受任何流式干扰

通过网络流式传输相同的视频优化延迟

重要的是流不会干扰正在写入磁盘的视频,因为网络连接可能不稳定,例如WiFi路由器可能超出范围等。

要做到这一点,我尝试的第一件事是:

#Receiver side

FPS="30"

netcat -l -p 5000 | mplayer -vf scale -zoom -xy 1280 -fps $FPS -cache-min 50 -cache 1024 - &

#RPi side

FPS="30"

mkfifo netcat_fifo

raspivid -t 0 -md 5 -fps $FPS -o - | tee --output-error=warn netcat_fifo > $video_out &

cat netcat_fifo | netcat -v 192.168.0.101 5000 &> $netcat_log &

流媒体工作得非常好。但是,当我关闭路由器,模拟网络问题时,我的$ video_out被切断了。我认为这是由于netcat_fifo的反压。

我在stackexchange找到了一个关于非阻塞FIFO的解决方案,通过用ftee替换tee:

它现在阻止我的$ video_out受到流媒体的影响,但是流媒体本身非常不稳定。最好的结果是使用以下脚本:

#RPi side

FPS="30"

MULTIPIPE="ftee"

mkfifo netcat_fifo

raspivid -t 0 -md 5 -fps $FPS -o - | ./${MULTIPIPE} netcat_fifo > $video_out &

cat netcat_fifo | mbuffer --direct -t -s 2k 2> $mbuffer_log | netcat -v 192.168.0.101 5000 &> $netcat_log &

当我检查mbuffer日志时,我诊断出一个大部分时间仍为空的FIFO,但具有99-100%利用率的峰值。在这些峰值期间,我的mplayer接收方在解码视频时有很多错误,需要大约5秒才能恢复。在此间隔之后,mbuffer日志再次显示空FIFO。 empty-> full-> empty继续打开。

我有两个问题:

我使用正确的方法来解决我的问题吗?

如果是这样,我如何在保持$ video_out文件完整的同时渲染我的流媒体更强大?

答案

我有一点尝试,它似乎在我的Raspberry Pi 3上非常稳定地工作。它评论很好,所以它应该很容易理解,但你可以随时询问是否有任何问题。

基本上有3个线程:

主程序 - 它不断从stdin读取它的raspivid并循环地将数据放入一堆缓冲区

磁盘写入程序线程 - 它不断循环遍历缓冲区列表,等待下一个缓冲区变满。当缓冲区已满时,它会将内容写入磁盘,将缓冲区标记为已写入并移至下一个缓冲区

fifo writer线程 - 它不断循环遍历缓冲区列表,等待下一个缓冲区变满。当缓冲区已满时,它会将内容写入fifo,刷新fifo以减少滞后并将缓冲区标记为已写入并移至下一个缓冲区。错误被忽略。

所以,这是代码:

// main.cpp

// Mark Setchell

//

// Read video stream from "raspivid" and write (independently) to both disk file

// and stdout - for onward netcatting to another host.

//

// Compiles with:

// g++ main.cpp -o main -lpthread

//

// Run on Raspberry Pi with:

// raspivid -t 0 -md 5 -fps 30 -o - | ./main video.h264 | netcat -v 192.168.0.8 5000

//

// Receive on other host with:

// netcat -l -p 5000 | mplayer -vf scale -zoom -xy 1280 -fps 30 -cache-min 50 -cache 1024 -

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define BUFSZ 65536

#define NBUFS 64

class Buffer{

public:

int bytes=0;

std::atomic NeedsWriteToDisk{0};

std::atomic NeedsWriteToFifo{0};

unsigned char data[BUFSZ];

};

std::vector buffers(NBUFS);

// This is the DiskWriter thread.

// It loops through all the buffers waiting in turn for each one to become ready

// then writes it to disk and marks the buffer as written before moving to next

// buffer.

void DiskWriter(char* filename){

int bufIndex=0;

// Open output file

int fd=open(filename,O_CREAT|O_WRONLY|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);

if(fd==-1)

{

std::cerr << "ERROR: Unable to open output file" << std::endl;

exit(EXIT_FAILURE);

}

bool Error=false;

while(!Error){

// Wait for buffer to be filled by main thread

while(buffers[bufIndex].NeedsWriteToDisk!=1){

// std::this_thread::sleep_for(std::chrono::milliseconds(1));

}

// Write to disk

int bytesToWrite=buffers[bufIndex].bytes;

int bytesWritten=write(fd,reinterpret_cast(&buffers[bufIndex].data),bytesToWrite);

if(bytesWritten!=bytesToWrite){

std::cerr << "ERROR: Unable to write to disk" << std::endl;

exit(EXIT_FAILURE);

}

// Mark buffer as written

buffers[bufIndex].NeedsWriteToDisk=0;

// Move to next buffer

bufIndex=(bufIndex+1)%NBUFS;

}

}

// This is the FifoWriter thread.

// It loops through all the buffers waiting in turn for each one to become ready

// then writes it to the Fifo, flushes it for reduced lag, and marks the buffer

// as written before moving to next one. Errors are ignored.

void FifoWriter(){

int bufIndex=0;

bool Error=false;

while(!Error){

// Wait for buffer to be filled by main thread

while(buffers[bufIndex].NeedsWriteToFifo!=1){

// std::this_thread::sleep_for(std::chrono::milliseconds(1));

}

// Write to fifo

int bytesToWrite=buffers[bufIndex].bytes;

int bytesWritten=write(STDOUT_FILENO,reinterpret_cast(&buffers[bufIndex].data),bytesToWrite);

if(bytesWritten!=bytesToWrite){

std::cerr << "ERROR: Unable to write to fifo" << std::endl;

}

// Try to reduce lag

fflush(stdout);

// Mark buffer as written

buffers[bufIndex].NeedsWriteToFifo=0;

// Move to next buffer

bufIndex=(bufIndex+1)%NBUFS;

}

}

int main(int argc, char *argv[])

{

int bufIndex=0;

if(argc!=2){

std::cerr << "ERROR: Usage " << argv[0] << " filename" << std::endl;

exit(EXIT_FAILURE);

}

char * filename = argv[1];

// Start disk and fifo writing threads in parallel

std::thread tDiskWriter(DiskWriter,filename);

std::thread tFifoWriter(FifoWriter);

bool Error=false;

// Continuously fill buffers from "raspivid" on stdin. Mark as full and

// needing output to disk and fifo before moving to next buffer.

while(!Error)

{

// Check disk writer is not behind before re-using buffer

if(buffers[bufIndex].NeedsWriteToDisk==1){

std::cerr << "ERROR: Disk writer is behind by " << NBUFS << " buffers" << std::endl;

}

// Check fifo writer is not behind before re-using buffer

if(buffers[bufIndex].NeedsWriteToFifo==1){

std::cerr << "ERROR: Fifo writer is behind by " << NBUFS << " buffers" << std::endl;

}

// Read from STDIN till buffer is pretty full

int bytes;

int totalBytes=0;

int bytesToRead=BUFSZ;

unsigned char* ptr=reinterpret_cast(&buffers[bufIndex].data);

while(totalBytes

bytes = read(STDIN_FILENO,ptr,bytesToRead);

if(bytes<=0){

Error=true;

break;

}

ptr+=bytes;

totalBytes+=bytes;

bytesToRead-=bytes;

}

// Signal buffer ready for writing

buffers[bufIndex].bytes=totalBytes;

buffers[bufIndex].NeedsWriteToDisk=1;

buffers[bufIndex].NeedsWriteToFifo=1;

// Move to next buffer

bufIndex=(bufIndex+1)%NBUFS;

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值