使用 live555 直播来自 v4l2 的摄像头图像
结合前面的 采集 v4l2 视频, 使用 live555, 通过 rtsp 发布实时流. capture.h, capture.cpp, vcompress.h, vcompress.cpp 需要参考前面几片文章. 这里仅仅贴出 v4l2_x264_service.cpp [cpp] view plaincopy #includestdio.h #includestdlib.h #includeunistd
结合前面的 采集 v4l2 视频, 使用 live555, 通过 rtsp 发布实时流. capture.h, capture.cpp, vcompress.h, vcompress.cpp 需要参考前面几片文章. 这里仅仅贴出 v4l2_x264_service.cpp
[cpp] view plaincopy
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
- #include "capture.h"
- #include "vcompress.h"
- static UsageEnvironment *_env = 0;
- #define SINK_PORT 3030
- #define VIDEO_WIDTH 320
- #define VIDEO_HEIGHT 240
- #define FRAME_PER_SEC 5.0
- pid_t gettid()
- {
- return syscall(SYS_gettid);
- }
- // 使用 webcam + x264
- class WebcamFrameSource : public FramedSource
- {
- void *mp_capture, *mp_compress; // v4l2 + x264 encoder
- int m_started;
- void *mp_token;
- public:
- WebcamFrameSource (UsageEnvironment &env)
- : FramedSource(env)
- {
- fprintf(stderr, "[%d] %s .... calling\n", gettid(), __func__);
- mp_capture = capture_open("/dev/video0", VIDEO_WIDTH, VIDEO_HEIGHT, PIX_FMT_YUV420P);
- if (!mp_capture) {
- fprintf(stderr, "%s: open /dev/video0 err\n", __func__);
- exit(-1);
- }
- mp_compress = vc_open(VIDEO_WIDTH, VIDEO_HEIGHT, FRAME_PER_SEC);
- if (!mp_compress) {
- fprintf(stderr, "%s: open x264 err\n", __func__);
- exit(-1);
- }
- m_started = 0;
- mp_token = 0;
- }
- ~WebcamFrameSource ()
- {
- fprintf(stderr, "[%d] %s .... calling\n", gettid(), __func__);
- if (m_started) {
- envir().taskScheduler().unscheduleDelayedTask(mp_token);
- }
- if (mp_compress)
- vc_close(mp_compress);
- if (mp_capture)
- capture_close(mp_capture);
- }
- protected:
- virtual void doGetNextFrame ()
- {
- if (m_started) return;
- m_started = 1;
- // 根据 fps, 计算等待时间
- double delay = 1000.0 / FRAME_PER_SEC;
- int to_delay = delay * 1000; // us
- mp_token = envir().taskScheduler().scheduleDelayedTask(to_delay,
- getNextFrame, this);
- }
[cpp] view plaincopy
- virtual unsigned maxFrameSize() const // 这个很重要, 如果不设置, 可能导致 getNextFrame() 出现 fMaxSize 小于实际编码帧的情况, 导致图像不完整
[cpp] view plaincopy
- { return 100*1024; }
[cpp] view plaincopy
- private:
- static void getNextFrame (void *ptr)
- {
- ((WebcamFrameSource*)ptr)->getNextFrame1();
- }
- void getNextFrame1 ()
- {
- // capture:
- Picture pic;
- if (capture_get_picture(mp_capture, &pic)
- fprintf(stderr, "==== %s: capture_get_picture err\n", __func__);
- m_started = 0;
- return;
- }
- // compress
- const void *outbuf;
- int outlen;
- if (vc_compress(mp_compress, pic.data, pic.stride, &outbuf, &outlen)
- fprintf(stderr, "==== %s: vc_compress err\n", __func__);
- m_started = 0;
- return;
- }
- int64_t pts, dts;
- int key;
- vc_get_last_frame_info(mp_compress, &key, &pts, &dts);
- // save outbuf
- gettimeofday(&fPresentationTime, 0);
- fFrameSize = outlen;
- if (fFrameSize > fMaxSize) {
- fNumTruncatedBytes = fFrameSize - fMaxSize;
- fFrameSize = fMaxSize;
- }
- else {
- fNumTruncatedBytes = 0;
- }
- memmove(fTo, outbuf, fFrameSize);
- // notify
- afterGetting(this);
- m_started = 0;
- }
- };
- class WebcamOndemandMediaSubsession : public OnDemandServerMediaSubsession
- {
- public:
- static WebcamOndemandMediaSubsession *createNew (UsageEnvironment &env, FramedSource *source)
- {
- return new WebcamOndemandMediaSubsession(env, source);
- }
- protected:
- WebcamOndemandMediaSubsession (UsageEnvironment &env, FramedSource *source)
- : OnDemandServerMediaSubsession(env, True) // reuse the first source
- {
- fprintf(stderr, "[%d] %s .... calling\n", gettid(), __func__);
- mp_source = source;
- mp_sdp_line = 0;
- }
- ~WebcamOndemandMediaSubsession ()
- {
- fprintf(stderr, "[%d] %s .... calling\n", gettid(), __func__);
- if (mp_sdp_line) free(mp_sdp_line);
- }
- private:
- static void afterPlayingDummy (void *ptr)
- {
- fprintf(stderr, "[%d] %s .... calling\n", gettid(), __func__);
- // ok
- WebcamOndemandMediaSubsession *This = (WebcamOndemandMediaSubsession*)ptr;
- This->m_done = 0xff;
- }
- static void chkForAuxSDPLine (void *ptr)
- {
- WebcamOndemandMediaSubsession *This = (WebcamOndemandMediaSubsession *)ptr;
- This->chkForAuxSDPLine1();
- }
- void chkForAuxSDPLine1 ()
- {
- fprintf(stderr, "[%d] %s .... calling\n", gettid(), __func__);
- if (mp_dummy_rtpsink->auxSDPLine())
- m_done = 0xff;
- else {
- int delay = 100*1000; // 100ms
- nextTask() = envir().taskScheduler().scheduleDelayedTask(delay,
- chkForAuxSDPLine, this);
- }
- }
- protected:
- virtual const char *getAuxSDPLine (RTPSink *sink, FramedSource *source)
- {
- fprintf(stderr, "[%d] %s .... calling\n", gettid(), __func__);
- if (mp_sdp_line) return mp_sdp_line;
- mp_dummy_rtpsink = sink;
- mp_dummy_rtpsink->startPlaying(*source, 0, 0);
- //mp_dummy_rtpsink->startPlaying(*source, afterPlayingDummy, this);
- chkForAuxSDPLine(this);
- m_done = 0;
- envir().taskScheduler().doEventLoop(&m_done);
- mp_sdp_line = strdup(mp_dummy_rtpsink->auxSDPLine());
- mp_dummy_rtpsink->stopPlaying();
- return mp_sdp_line;
- }
- virtual RTPSink *createNewRTPSink(Groupsock *rtpsock, unsigned char type, FramedSource *source)
- {
- fprintf(stderr, "[%d] %s .... calling\n", gettid(), __func__);
- return H264VideoRTPSink::createNew(envir(), rtpsock, type);
- }
- virtual FramedSource *createNewStreamSource (unsigned sid, unsigned &bitrate)
- {
- fprintf(stderr, "[%d] %s .... calling\n", gettid(), __func__);
- bitrate = 500;
- return H264VideoStreamFramer::createNew(envir(), new WebcamFrameSource(envir()));
- }
- private:
- FramedSource *mp_source; // 对应 WebcamFrameSource
- char *mp_sdp_line;
- RTPSink *mp_dummy_rtpsink;
- char m_done;
- };
- static void test_task (void *ptr)
- {
- fprintf(stderr, "test: task ....\n");
- _env->taskScheduler().scheduleDelayedTask(100000, test_task, 0);
- }
- static void test (UsageEnvironment &env)
- {
- fprintf(stderr, "test: begin...\n");
- char done = 0;
- int delay = 100 * 1000;
- env.taskScheduler().scheduleDelayedTask(delay, test_task, 0);
- env.taskScheduler().doEventLoop(&done);
- fprintf(stderr, "test: end..\n");
- }
- int main (int argc, char **argv)
- {
- // env
- TaskScheduler *scheduler = BasicTaskScheduler::createNew();
- _env = BasicUsageEnvironment::createNew(*scheduler);
- // test
- //test(*_env);
- // rtsp server
- RTSPServer *rtspServer = RTSPServer::createNew(*_env, 8554);
- if (!rtspServer) {
- fprintf(stderr, "ERR: create RTSPServer err\n");
- ::exit(-1);
- }
- // add live stream
- do {
- WebcamFrameSource *webcam_source = 0;
- ServerMediaSession *sms = ServerMediaSession::createNew(*_env, "webcam", 0, "Session from /dev/video0");
- sms->addSubsession(WebcamOndemandMediaSubsession::createNew(*_env, webcam_source));
- rtspServer->addServerMediaSession(sms);
- char *url = rtspServer->rtspURL(sms);
- *_env "using url \"" "\"\n";
- delete [] url;
- } while (0);
- // run loop
- _env->taskScheduler().doEventLoop();
- return 1;
- }
需要 live555 + libavcodec + libswscale + libx264, client 使用 vlc, mplayer, quicktime, .....

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

CrystalDiskMark是一款适用于硬盘的小型HDD基准测试工具,可以快速测量顺序和随机读/写速度。接下来就让小编为大家介绍一下CrystalDiskMark,以及crystaldiskmark如何使用吧~一、CrystalDiskMark介绍CrystalDiskMark是一款广泛使用的磁盘性能测试工具,用于评估机械硬盘和固态硬盘(SSD)的读写速度和随机I/O性能。它是一款免费的Windows应用程序,并提供用户友好的界面和各种测试模式来评估硬盘驱动器性能的不同方面,并被广泛用于硬件评

foobar2000是一款能随时收听音乐资源的软件,各种音乐无损音质带给你,增强版本的音乐播放器,让你得到更全更舒适的音乐体验,它的设计理念是将电脑端的高级音频播放器移植到手机上,提供更加便捷高效的音乐播放体验,界面设计简洁明了易于使用它采用了极简的设计风格,没有过多的装饰和繁琐的操作能够快速上手,同时还支持多种皮肤和主题,根据自己的喜好进行个性化设置,打造专属的音乐播放器支持多种音频格式的播放,它还支持音频增益功能根据自己的听力情况调整音量大小,避免过大的音量对听力造成损害。接下来就让小编为大

网易邮箱,作为中国网民广泛使用的一种电子邮箱,一直以来以其稳定、高效的服务赢得了用户的信赖。而网易邮箱大师,则是专为手机用户打造的邮箱软件,它极大地简化了邮件的收发流程,让我们的邮件处理变得更加便捷。那么网易邮箱大师该如何使用,具体又有哪些功能呢,下文中本站小编将为大家带来详细的内容介绍,希望能帮助到大家!首先,您可以在手机应用商店搜索并下载网易邮箱大师应用。在应用宝或百度手机助手中搜索“网易邮箱大师”,然后按照提示进行安装即可。下载安装完成后,我们打开网易邮箱账号并进行登录,登录界面如下图所示

在如今云存储已经成为我们日常生活和工作中不可或缺的一部分。百度网盘作为国内领先的云存储服务之一,凭借其强大的存储功能、高效的传输速度以及便捷的操作体验,赢得了广大用户的青睐。而且无论你是想要备份重要文件、分享资料,还是在线观看视频、听取音乐,百度网盘都能满足你的需求。但是很多用户们可能对百度网盘app的具体使用方法还不了解,那么这篇教程就将为大家详细介绍百度网盘app如何使用,还有疑惑的用户们就快来跟着本文详细了解一下吧!百度云网盘怎么用:一、安装首先,下载并安装百度云软件时,请选择自定义安装选

小红书都是一款你们非常熟悉的生活社区平台应用,这里的功能多多,真的都能够让大家任何的时候,都能看到各种各样的资讯内容,笔记十分多,图文并茂的,都能够让大家非常的满意,且有些时候都能够看到一些直播间的,所以大家也是产生了自己想开通直播的,能和大家一起聊聊天的,却不知道怎么开通直播的,下面小编也能给你们带来具体的操作方法,希望能够帮助到你们的。小红书开启直播方法: 1、首先打开小红书,点击首页底部的+。 2、然后切换至直播,点击开始直播入口。

MetaMask(中文也叫小狐狸钱包)是一款免费的、广受好评的加密钱包软件。目前,BTCC已支持绑定MetaMask钱包,绑定后可使用MetaMask钱包进行快速登入,储值、买币等,且首次绑定还可获得20USDT体验金。在BTCCMetaMask钱包教学中,我们将详细介绍如何注册和使用MetaMask,以及如何在BTCC绑定并使用小狐狸钱包。MetaMask钱包是什么?MetaMask小狐狸钱包拥有超过3,000万用户,是当今最受欢迎的加密货币钱包之一。它可免费使用,可作为扩充功能安装在网络

小伙伴们知道PotPlayer怎么看直播吗?今天小编就来讲解PotPlayer看直播教程,感兴趣的快跟小编一起来看看吧,希望能够帮助到大家呢。首先我们打开PotPlayer,然后选择软件右下角的多功能侧边栏,然后我们进行点击;参考下图然后会弹出一个播放列表,我们可以选择“添加”选项,在这里可以对直播的设置进行调整和添加。这时候我们在弹出的下拉框中,我们选择添加链接,当然如果我们有直播源的文件的话,我们直接选择添加文件,然后将文件进行导入也是可以的。然后我们在弹出来的地址框中,我们输入自己想要观看

小米汽车软件提供远程车控功能,让用户可以通过手机或电脑远程控制车辆,例如开关车辆的门窗、启动引擎、控制车辆的空调和音响等,下文就是这个软件的使用及内容,一起了解下吧。小米汽车app功能及使用方法大全1、小米汽车app在3月25日上线苹果AppStore,现在安卓手机的应用商店中也可以下载了;购车:了解小米汽车核心亮点和技术参数,可预约试驾、配置订购您的小米汽车,支持在线处理提车待办事项。3、社区:了解小米汽车品牌资讯,交流用车体验,分享精彩车生活;4、车控:手机就是遥控器,远程控制,实时安防,轻
