結合前面的 採集 v4l2 視頻, 使用 live555, 通過 rtsp 發布即時資料流. capture.h, capture.cpp, vcompress.h, vcompress.cpp 需要參考前面幾片文章. 這裡僅僅貼出 v4l2_x264_service.cpp
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <assert.h>#include <liveMedia.hh>#include <BasicUsageEnvironment.hh>#include <GroupsockHelper.hh>#include <sys/types.h>#include <sys/syscall.h>#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.0pid_t gettid(){return syscall(SYS_gettid);}// 使用 webcam + x264class WebcamFrameSource : public FramedSource{void *mp_capture, *mp_compress;// v4l2 + x264 encoderint 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;// usmp_token = envir().taskScheduler().scheduleDelayedTask(to_delay,getNextFrame, this);}
virtual unsigned maxFrameSize() const // 這個很重要, 如果不設定, 可能導致 getNextFrame() 出現 fMaxSize 小於實際編碼幀的情況, 導致映像不完整
{ return 100*1024; }
private:static void getNextFrame (void *ptr){((WebcamFrameSource*)ptr)->getNextFrame1();}void getNextFrame1 (){// capture:Picture pic;if (capture_get_picture(mp_capture, &pic) < 0) {fprintf(stderr, "==== %s: capture_get_picture err\n", __func__);m_started = 0;return;}// compressconst void *outbuf;int outlen;if (vc_compress(mp_compress, pic.data, pic.stride, &outbuf, &outlen) < 0) {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 outbufgettimeofday(&fPresentationTime, 0);fFrameSize = outlen;if (fFrameSize > fMaxSize) {fNumTruncatedBytes = fFrameSize - fMaxSize;fFrameSize = fMaxSize;}else {fNumTruncatedBytes = 0;}memmove(fTo, outbuf, fFrameSize);// notifyafterGetting(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__);// okWebcamOndemandMediaSubsession *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;// 100msnextTask() = 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;// 對應 WebcamFrameSourcechar *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){// envTaskScheduler *scheduler = BasicTaskScheduler::createNew();_env = BasicUsageEnvironment::createNew(*scheduler);// test//test(*_env);// rtsp serverRTSPServer *rtspServer = RTSPServer::createNew(*_env, 8554);if (!rtspServer) {fprintf(stderr, "ERR: create RTSPServer err\n");::exit(-1);}// add live streamdo {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 \"" << url << "\"\n";delete [] url;} while (0);// run loop_env->taskScheduler().doEventLoop();return 1;}
需要 live555 + libavcodec + libswscale + libx264, client 使用 vlc, mplayer, quicktime, .....