使用 live555 直播来自 v4l2 的摄像头图像

结合前面的 采集 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.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);
	}
        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;
		}

		// compress
		const 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 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 \"" << url << "\"\n";
		delete [] url;
	} while (0);

	// run loop
	_env->taskScheduler().doEventLoop();

	return 1;
}

需要 live555 + libavcodec + libswscale + libx264, client 使用 vlc, mplayer, quicktime, .....

你可能感兴趣的:(video,url,Class,token,sms,delay)