Gstreamer学习与systemV 共享内存实例

前言

最近在做Gstreamer视频传输过程中,因涉及到多进程间的通信,本来使用本地socket方式进行传输,发现每秒的帧率会被降低,因此决定用共享内存试一下,看看是否能够快一点。经过多番文档阅读,发现存在syetemV与POSIX两种方式的共享内存,并且区别还是蛮明显的,因此决定两者都试一下。
由于systemV共享内存之间并没有进程同步,因此还需要信号量进行进程间同步操作,systemV的信号量比之POSIX信号量又有很大不同,因此采用SystemV信号量为SystemV共享内存做进程同步操作。实际需求如下:

需求说明

  • 进程A:Gstreamer进程,使用appsrc、vpuenc_h264、appsink三个元件对视频进行H264编码,appsrc通过元件的信号获取NV12格式的图像数据,vpuenc_h264元件进行编码,appsink获取编码后的H264帧、appsink的信号回调函数负责通过共享内存与信号量,将数据传输到进程B
  • 进程B:视频推动进程,通过共享内存获取H264视频帧,使用rtmp方式推送到服务端

进程A

进程A还有一个ipu_thread线程,负责将RGB32格式的数据转换为NV12格式,其与appsrc之间通过POSIX信号量进行同步。
进程A还通过SystemV信号量实现了一个二值信号量封装,用来进行进程间同步

  • int InitSemAvilable(int semId, int semNum)
  • int InitSemInUse(int semId, int semNum)
  • int WaitSem(int semId, int semNum)
  • int PostSem(int semId, int semNum)
  • int DeleteSem(int semId, int semNum)
#include <gst/gst.h>
#include <gst/app/gstappsrc.h>
#include <gst/app/gstappsink.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/un.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <pthread.h>
#include <semaphore.h>
#include <ipu.h>

typedef struct _GstDataStruct{
	GstElement *pipeline;
	GstElement *app_source;
	GstElement *video_convert;
	GstElement *h264_encoder;
	GstElement *app_sink;
	guint sourceid;        	// To control the GSource
	guint app_src_index;
	guint app_sink_index;
	guint bus_watch_id;
	GstBus *bus;
	GMainLoop *loop;  		// GLib's Main Loop
	int frame_rate;
	int gop_size;
	int bit_rate;
	int quant;
} GstDataStruct;

typedef struct
{
	struct fb_var_screeninfo vinfo;	// 可变的STOP显示屏幕信息
	struct fb_fix_screeninfo finfo;
	int width;					// width 宽度
	int height;					// height 高度
	int bpp;					// bit per pixel 像素的位数
	int rowsize;				// 屏幕一行所占字节数
	int real_len;				// 实际显示区域的字节数
	int total_len;				// 显示区域总字节数,即长度
	int offset;					// 实际显示位置在总长度中的偏移量
}FbInfoStruct;


#define VIDEO_BUF_SIZE	((128 * 1024 * 1024) - sizeof(unsigned int)*3)
typedef struct
{
	unsigned int head;
	unsigned int len;
	unsigned int index;
	unsigned char data[VIDEO_BUF_SIZE];
}ShmVideo360Struct;

union semun
{
	int val;
	struct semid_ds *buf;
	unsigned short int *array;
	struct seminfo *__buf;
};

static int fb_init(void);
static int gst_pipeline_init(void);
static void new_h264_sample_on_appsink (GstElement *sink, GstDataStruct *pGstData);
static void start_feed (GstElement * pipeline, guint size, GstDataStruct *pGstData);
static void stop_feed (GstElement * pipeline, GstDataStruct *pGstData);
static gboolean bus_msg_call(GstBus *bus, GstMessage *msg, GstDataStruct *pGstData);
static void *ipu_thread(void *arg);

struct IPU_PHY_MEM {
	char *vaddr;
	unsigned int paddr;
	unsigned int size;
};

struct ipu_task gtask;
struct IPU_PHY_MEM ipu_pmem;
static GstDataStruct GstData;
static FbInfoStruct FbInfo;

int fd_fb;
int fd_ipu;
sem_t sem_frame_put;
sem_t sem_frame_get;

#define UNIX_DOMAIN "/home/root/UNIX.domain"
#define SHM_KEY		(0x8765)
#define SEM_KEY		(0x4321)
#define OBJ_PERMS	(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
int shmid;									//共享内存标识符
void *shm = NULL;							//分配的共享内存的原始首地址
ShmVideo360Struct *shared360 = NULL;		//指向shm

int semid;
#define USER_SEM_UNDO (FALSE)		//进程终止时,是否撤销对sem的操作
#define RETRY_ON_EINTR (FALSE)		//semop操作被信号中断时是否重试
#define WRITE_SEM		(0)
#define READ_SEM		(1)

int InitSemAvilable(int semId, int semNum)
{
	union semun arg;
	arg.val = 1;
	return semctl(semId, semNum, SETVAL, arg);
}

int InitSemInUse(int semId, int semNum)
{
	union semun arg;
	arg.val = 0;
	return semctl(semId, semNum, SETVAL, arg);
}

int WaitSem(int semId, int semNum)
{
	struct sembuf sops;

	sops.sem_num = semNum;
	sops.sem_op = -1;
	sops.sem_flg = USER_SEM_UNDO ? SEM_UNDO : 0;
	while(semop(semId, &sops, 1) == -1)
	{
		if(errno != EINTR || !RETRY_ON_EINTR)
			return -1;
	}
	return 0;
}

int PostSem(int semId, int semNum)
{
	struct sembuf sops;

	sops.sem_num = semNum;
	sops.sem_op = 1;
	sops.sem_flg = USER_SEM_UNDO ? SEM_UNDO : 0;
	return semop(semId, &sops, 1);
}
int DeleteSem(int semId, int semNum)
{
	union semun arg;
	return semctl(semId, semNum, IPC_RMID, arg);
}

int DeleteShm(int shmid, void* shm)
{
	if(shmdt(shm) == -1)									//把共享内存从当前进程中分离
	{
		printf("shmdt failed\n");
		return -1;
	}

	if(shmctl(shmid, IPC_RMID, 0) == -1)					//删除共享内存
	{
		printf("shmctl(IPC_RMID) failed\n");
		return -1;
	}
	return 0;
}

int main(int argc, char *argv[])
{
	int ret = 0;
	pthread_t tid;

	printf("================ imx60 360 main start =============\n");
	memset (&GstData, 0, sizeof (GstDataStruct));
	sem_init(&sem_frame_put, 0, 0);
	sem_init(&sem_frame_get, 0, 0);

	if(argc == 5)
	{
		GstData.frame_rate = atoi(argv[1]);
		GstData.gop_size = atoi(argv[2]);
		GstData.bit_rate = atoi(argv[3]);
		GstData.quant = atoi(argv[4]);
	}
	else
	{
		GstData.frame_rate = 30;
		GstData.gop_size = 30;
		GstData.bit_rate = 1800;
		GstData.quant = 1;
	}
	printf("frame_rate:%d, gop_size:%d, bit_rate:%d ,quant:%d!\n",
		   GstData.frame_rate, GstData.gop_size, GstData.bit_rate, GstData.quant);

	while(1)
	{
		shmid = shmget((key_t)SHM_KEY, 0, 0);	//获得已存在的共享内存
		if(shmid != -1) break;
		printf("shmget failed, try again, 1 second later!\n");
		sleep(2);
	}
	errno = 0;
	shm = shmat(shmid, NULL, 0);				//将共享内存连接到当前进程的地址空间
	if(shm == (void*)-1)
	{
		printf("shmat failed\n");
		printf("errno info: %s\n", strerror(errno));
		return 0;
	}
	printf("Memory attached at %X\n", (int)shm);
	shared360 = (ShmVideo360Struct*)shm;
	errno = 0;
	semid = semget((key_t)SEM_KEY, 0, 0);	//获得已存在的信号量
	if(semid == -1)
	{
		printf("semget failed\n");
		printf("errno info: %s\n", strerror(errno));
		return 0;
	}

	fb_init();
	ret = pthread_create(&tid, NULL, ipu_thread, NULL);
	if(ret)
	{
		printf("create gst_thread failed, error = %d \n", ret);
		close(fd_fb);
		close(fd_ipu);
		return -1;
	}
	printf("create ipu_thread (thr ID = 0x%x) OK.\n", (unsigned int)tid);

	if(gst_pipeline_init() != 0)
	{
		gst_object_unref (GstData.pipeline);
		close(fd_fb);
		close(fd_ipu);
		return -1;
	}

	printf("pipeline start to playing!\n");
	gst_element_set_state (GstData.pipeline, GST_STATE_PLAYING);
	GstData.loop = g_main_loop_new(NULL, FALSE);	// Create gstreamer loop
	g_main_loop_run(GstData.loop);					// Loop will run until receiving EOS (end-of-stream), will block here
	printf("g_main_loop_run returned, stopping record\n");
	gst_element_set_state (GstData.pipeline, GST_STATE_NULL);		// Stop pipeline to be released
	printf("Deleting pipeline\n");
	gst_object_unref (GstData.pipeline);							// THis will also delete all pipeline elements
	g_source_remove(GstData.bus_watch_id);
	g_main_loop_unref(GstData.loop);

	close(fd_fb);
	close(fd_ipu);
	sem_destroy(&sem_frame_put);
	sem_destroy(&sem_frame_get);

	printf("================ imx60 360 main end ==============\n");
	return 0;
}
static void *ipu_thread(void *arg)
{
	int res;
	unsigned int yuv_size = 0;
	unsigned int ipu_index = 0;

	sleep(2);
	yuv_size = FbInfo.vinfo.xres * FbInfo.vinfo.yres * 3 / 2;	// NV12 size
	printf("yuv_size:%d\n", yuv_size);
	ipu_pmem.paddr = yuv_size;
	ipu_pmem.size = yuv_size;
	printf("before IPU_ALLOC pmem.paddr:%d\n", ipu_pmem.paddr);
	res = ioctl(fd_ipu, IPU_ALLOC, &ipu_pmem.paddr);
	printf("after IPU_ALLOC pmem.paddr:%d\n", ipu_pmem.paddr);
	ipu_pmem.vaddr = mmap(NULL, yuv_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_ipu, ipu_pmem.paddr);
	printf("pmem.vaddr:%d\n", (int)ipu_pmem.vaddr);

	bzero(&gtask, sizeof(gtask));
	gtask.input.width  = FbInfo.vinfo.xres;
	gtask.input.height = FbInfo.vinfo.yres;
	gtask.input.crop.w = FbInfo.vinfo.xres;
	gtask.input.crop.h = FbInfo.vinfo.yres;
	if(FbInfo.vinfo.bits_per_pixel == 16)
		gtask.input.format = IPU_PIX_FMT_RGB565;
	else
		gtask.input.format = IPU_PIX_FMT_BGR32;
	FbInfo.offset = FbInfo.vinfo.xres * (FbInfo.vinfo.bits_per_pixel >> 3) * FbInfo.vinfo.yoffset;
	gtask.input.paddr = FbInfo.finfo.smem_start + FbInfo.offset;
	gtask.output.width = FbInfo.vinfo.xres;
	gtask.output.height = FbInfo.vinfo.yres;
	gtask.output.crop.w = FbInfo.vinfo.xres;
	gtask.output.crop.h = FbInfo.vinfo.yres;
	gtask.output.format = IPU_PIX_FMT_NV12;
	gtask.output.paddr = ipu_pmem.paddr;

	gtask.priority = IPU_TASK_PRIORITY_HIGH;
	gtask.task_id = IPU_TASK_ID_PP;
	while(res != IPU_CHECK_OK)
	{
		printf("IPU check task!\n");
		res = ioctl(fd_ipu, IPU_CHECK_TASK, &gtask);
	}
	while(1)
	{
		ipu_index++;
		printf("ipu_index:%d\n",ipu_index);
		res = ioctl(fd_ipu, IPU_QUEUE_TASK, &gtask);
		if(res < 0)
		{
			printf("IPU queue task failed\n");
		}
		else
		{
			sem_post(&sem_frame_put);
			sem_wait(&sem_frame_get);
			ioctl(fd_fb, FBIOGET_VSCREENINFO, &FbInfo.vinfo);
			//ioctl(fd_fb, FBIOGET_FSCREENINFO, &FbInfo.finfo);
			FbInfo.offset = FbInfo.vinfo.xres * (FbInfo.vinfo.bits_per_pixel >> 3) * FbInfo.vinfo.yoffset;
			gtask.input.paddr = FbInfo.finfo.smem_start + FbInfo.offset;
		}
	}
	munmap(ipu_pmem.vaddr, ipu_pmem.size);
	ioctl(fd_ipu, IPU_FREE, &ipu_pmem.paddr);
	return 0;
}

static int fb_init(void)
{
	printf("=========== imx60 360 get fb0 info start ==========\n");
	memset (&FbInfo, 0, sizeof (FbInfoStruct));

	fd_ipu = open("/dev/mxc_ipu", O_RDWR);
	fd_fb = open("/dev/fb0", O_RDWR);
	ioctl(fd_fb, FBIOGET_VSCREENINFO, &FbInfo.vinfo);
	ioctl(fd_fb, FBIOGET_FSCREENINFO, &FbInfo.finfo);

	FbInfo.width = FbInfo.vinfo.xres;
	FbInfo.height = FbInfo.vinfo.yres;
	FbInfo.bpp = FbInfo.vinfo.bits_per_pixel;
	FbInfo.rowsize = FbInfo.width * (FbInfo.bpp >> 3);
	FbInfo.offset = FbInfo.rowsize * FbInfo.vinfo.yoffset;
	FbInfo.total_len = FbInfo.vinfo.xres_virtual * FbInfo.vinfo.yres_virtual * (FbInfo.bpp >> 3);
	FbInfo.real_len = FbInfo.width * FbInfo.height * (FbInfo.bpp >> 3);
	printf("================= var screen info =================\n");
	printf("  sz  [%d x %d] %d\n", FbInfo.width, FbInfo.height, FbInfo.bpp);
	printf("  vsz [%d x %d]\n", FbInfo.vinfo.xres_virtual, FbInfo.vinfo.yres_virtual);
	printf("  pan     : (%d, %d)\n", FbInfo.vinfo.xoffset, FbInfo.vinfo.yoffset);
	printf("  off     : %d\n", FbInfo.offset);
	printf("============ imx60 360 get fb0 info end ===========\n");
	return 0;
}

static int gst_pipeline_init(void)
{
	printf("============= imx60 360 gst init start ============\n");
	gst_init (NULL, NULL);
	printf("=========== create imx60 360 pipeline =============\n");
	GstData.pipeline           = gst_pipeline_new ("imx60_360");
	GstData.app_source         = gst_element_factory_make ("appsrc",            "src");
	GstData.h264_encoder       = gst_element_factory_make ("vpuenc_h264",       "video-encoder");
	GstData.app_sink           = gst_element_factory_make ("appsink",           "fake");
	if (!GstData.pipeline || !GstData.app_source || !GstData.h264_encoder || !GstData.app_sink)
	{
		g_printerr ("One element could not be created... Exit\n");
		return -1;
	}

	printf("============ link imx60 360 pipeline ==============\n");
	char szTemp[64];
	sprintf(szTemp, "%d", FbInfo.vinfo.xres * FbInfo.vinfo.yres * 3 / 2);	// NV12 size
	g_object_set(G_OBJECT(GstData.app_source), "blocksize", szTemp, NULL);
	g_object_set(G_OBJECT(GstData.app_source), "size", szTemp, NULL);
	g_object_set(G_OBJECT(GstData.app_source), "do-timestamp", TRUE, NULL);
	g_object_set(G_OBJECT(GstData.app_source), "stream-type", 0, "format", GST_FORMAT_TIME, NULL);
	g_object_set(G_OBJECT(GstData.app_source), "min-percent", 0, "typefind", TRUE, NULL);

	GstCaps *caps_appsrc;
	caps_appsrc = gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING,"NV12",
			   	   	   	   	"width", G_TYPE_INT, FbInfo.width,
							"height", G_TYPE_INT, FbInfo.height,
							"framerate",GST_TYPE_FRACTION, GstData.frame_rate, 1, NULL);
	GstCaps *caps_app_sink;
	caps_app_sink = gst_caps_new_simple("video/x-h264", "stream-format", G_TYPE_STRING, "byte-stream",
		                    "alignment", G_TYPE_STRING, "au", NULL);
	g_object_set(G_OBJECT(GstData.app_source), "caps", caps_appsrc, NULL);

	g_object_set(G_OBJECT(GstData.h264_encoder), "bitrate", GstData.bit_rate, NULL);
	g_object_set(G_OBJECT(GstData.h264_encoder), "quant", GstData.quant, NULL);
	g_object_set(G_OBJECT(GstData.h264_encoder), "gop-size", GstData.gop_size, NULL);
	g_object_set(G_OBJECT(GstData.app_sink), "emit-signals", TRUE, "caps", caps_app_sink, "sync", TRUE, NULL);

	GstData.bus = gst_pipeline_get_bus(GST_PIPELINE(GstData.pipeline));
	GstData.bus_watch_id = gst_bus_add_watch(GstData.bus, (GstBusFunc)bus_msg_call, (gpointer)&GstData);
	gst_object_unref(GstData.bus);
	g_signal_connect(GstData.app_source, "need-data", G_CALLBACK(start_feed), &GstData);
	g_signal_connect(GstData.app_source, "enough-data", G_CALLBACK(stop_feed), &GstData);
	g_signal_connect(GstData.app_sink, "new-sample", G_CALLBACK(new_h264_sample_on_appsink), &GstData);
	gst_bin_add_many(GST_BIN(GstData.pipeline), GstData.app_source, GstData.h264_encoder, GstData.app_sink, NULL);

	if(gst_element_link(GstData.app_source, GstData.h264_encoder) != TRUE)
	{
		g_printerr ("GstData.app_source could not link GstData.h264_encoder\n");
		gst_object_unref (GstData.pipeline);
		return -1;
	}
	if(gst_element_link_filtered(GstData.h264_encoder, GstData.app_sink, caps_app_sink) != TRUE)
	{
		g_printerr ("GstData.h264_encoder could not link GstData.app_sink\n");
		gst_object_unref (GstData.pipeline);
		return -1;
	}
	gst_caps_unref (caps_app_sink);
	gst_caps_unref (caps_appsrc);
	return 0;
}

static void new_h264_sample_on_appsink (GstElement *sink, GstDataStruct *pGstData)
{
	int ret = 0;
	GstSample *sample = NULL;
	g_signal_emit_by_name (sink, "pull-sample", &sample);
	if(sample)
	{
		pGstData->app_sink_index++;
		GstBuffer *buffer = gst_sample_get_buffer(sample);
		GstMapInfo info;
		if(gst_buffer_map((buffer), &info, GST_MAP_READ))
		{
			ret = WaitSem(semid, WRITE_SEM);
			if ((shared360->head + info.size) > VIDEO_BUF_SIZE)
			{
				printf("video run a loop %d++++++++++++++++++++++++++++++++++\n", shared360->head);
				shared360->head = 0;
			}
			memcpy(&shared360->data[shared360->head], info.data, info.size);
			shared360->len = info.size;
			shared360->index++;
			g_print ("h264 frame is put to shm buffer, len:%d, index:%d\n", (int)info.size, pGstData->app_sink_index);
			ret = PostSem(semid, READ_SEM);
			if(ret == -1)
			{
				printf("SEM error, end of stream!\n");
				g_signal_emit_by_name (pGstData->app_source, "end-of-stream", &ret);
			}
			gst_buffer_unmap(buffer, &info);
		}
		gst_sample_unref(sample);
	}
}

static void start_feed (GstElement * pipeline, guint size, GstDataStruct *pGstData)
{
	GstFlowReturn ret;
	GstBuffer *buffer;
	GstMemory *memory;

	pGstData->app_src_index++;
	buffer = gst_buffer_new();
	sem_wait(&sem_frame_put);
	memory = gst_memory_new_wrapped(GST_MEMORY_FLAG_READONLY, ipu_pmem.vaddr, ipu_pmem.size, 0, ipu_pmem.size, NULL, NULL);
	gst_buffer_append_memory (buffer, memory);
	g_signal_emit_by_name (pGstData->app_source, "push-buffer", buffer, &ret);
	//GST_DEBUG ("feed buffer %p, offset %" G_GUINT64_FORMAT "-%u", buffer,FbInfo.offset, FbInfo.real_len);
	gst_buffer_unref(buffer);
	printf("pGstData->app_src_index:%d\n",pGstData->app_src_index);
	sem_post(&sem_frame_get);
}

static void stop_feed(GstElement * pipeline, GstDataStruct *pGstData)
{
	g_print("stop feed...................\n");
//	if (pGstData->sourceid != 0)
//	{
//		//GST_DEBUG ("stop feeding");
//		g_source_remove (pGstData->sourceid);
//		pGstData->sourceid = 0;
//	}
}

// Bus messages processing, similar to all gstreamer examples
gboolean bus_msg_call(GstBus *bus, GstMessage *msg, GstDataStruct *pGstData)
{
	gchar *debug;
	GError *error;
	GMainLoop *loop = pGstData->loop;

	GST_DEBUG ("got message %s",gst_message_type_get_name (GST_MESSAGE_TYPE (msg)));
	switch (GST_MESSAGE_TYPE(msg))
	{
		case GST_MESSAGE_EOS:
			printf("End of stream\n");
			g_main_loop_quit(loop);
			break;
		case GST_MESSAGE_ERROR:
			gst_message_parse_error(msg, &error, &debug);
			g_free(debug);
			g_printerr("Error: %s\n", error->message);
			g_error_free(error);
			g_main_loop_quit(loop);
			break;
		default:
			break;
	}
	return TRUE;
}

进程B

进程B这里只贴出与共享内存有关的线程,共享内存与信号量都有进程B创建并初始化

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/un.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
#include "media.h"
#include "srs_librtmp.h"
#include "queue.h"

#define VIDEO_BUF_SIZE	((128 * 1024 * 1024) - sizeof(unsigned int)*3)
typedef struct
{
	unsigned int head;
	unsigned int len;
	unsigned int index;
	unsigned char data[VIDEO_BUF_SIZE];
}ShmVideo360Struct;

union semun
{
	int val;
	struct semid_ds *buf;
	unsigned short int *array;
	struct seminfo *__buf;
};

extern unsigned long q_record;
extern int is_iframe(char p);
extern int read_h264_frame(char* data, int size, char** pp, int* pnb_start_code, int fps,
						   char** frame, int* frame_size, int* dts, int* pts);

#define UNIX_DOMAIN "/home/root/UNIX.domain"

unsigned long q_srs_rtmp360;					// add by luke zhao 2018.6.13, used for 360 video
srs_rtmp_t rtmp360 = NULL;						// add by luke zhao 2018.6.13, used for 360 video
pthread_mutex_t rtmp_lock360;					// add by luke zhao 2018.6.13, used for 360 video

int start_wait_iframe360 = 1;					// add by luke zhao 2018.6.13, used for 360 video
int rtmp_stop360 = 1;							// add by luke zhao 2018.6.13, used for 360 video
int rtmp360_had_start = 0;						// add by luke zhao 2018.6.13, used for 360 video
int rtmp360_wait_I = 1;							// add by luke zhao 2018.6.13, used for 360 video
char rtmp_url360[128] = {0};					// add by luke zhao 2018.6.13, used for 360 video
struct timeval tvl_360;							// add by luke zhao 2018.6.14, used for 360 video,360 frame has no time stamp,use ch0

int pretime360 = 0;
int queue_enough_360 = 0;
int rcv360startflag = 1;
extern int video_need_backup_360;
extern unsigned long q_backup_360_record;
extern pthread_cond_t bk_wait_360;

#define SHM_KEY		(0x8765)
#define SEM_KEY		(0x4321)
#define OBJ_PERMS	(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
int shmid;									//共享内存标识符
void *shm = NULL;							//分配的共享内存的原始首地址
ShmVideo360Struct *shared360 = NULL;		//指向shm

int semid;
#define USER_SEM_UNDO 	(0) 		//进程终止时,是否撤销对sem的操作
#define RETRY_ON_EINTR 	(0)		//semop操作被信号中断时是否重试
#define WRITE_SEM		(0)
#define READ_SEM		(1)

int InitSemAvilable(int semId, int semNum)
{
	union semun arg;
	arg.val = 1;
	return semctl(semId, semNum, SETVAL, arg);
}

int InitSemInUse(int semId, int semNum)
{
	union semun arg;
	arg.val = 0;
	return semctl(semId, semNum, SETVAL, arg);
}

int WaitSem(int semId, int semNum)
{
	struct sembuf sops;

	sops.sem_num = semNum;
	sops.sem_op = -1;
	sops.sem_flg = USER_SEM_UNDO ? SEM_UNDO : 0;
	while(semop(semId, &sops, 1) == -1)
	{
		if(errno != EINTR || !RETRY_ON_EINTR)
			return -1;
	}
	return 0;
}

int PostSem(int semId, int semNum)
{
	struct sembuf sops;

	sops.sem_num = semNum;
	sops.sem_op = 1;
	sops.sem_flg = USER_SEM_UNDO ? SEM_UNDO : 0;
	return semop(semId, &sops, 1);
}
int DeleteSem(int semId, int semNum)
{
	union semun arg;
	return semctl(semId, semNum, IPC_RMID, arg);
}

int DeleteShm(int shmid, void* shm)
{
	if(shmdt(shm) == -1)									//把共享内存从当前进程中分离
	{
		printf("shmdt failed\n");
		return -1;
	}

	if(shmctl(shmid, IPC_RMID, 0) == -1)					//删除共享内存
	{
		printf("shmctl(IPC_RMID) failed\n");
		return -1;
	}
	return 0;
}

void *thr_rcv_360(void *arg)
{
	int ret = 0;
	REC_MSG rec_msg;
	unsigned char *vbuf;
	unsigned int starttime;
	unsigned int endtime;
	REC_MSG tmp_rec_msg = {0};
	int backup_waked = 0;

	semid = semget((key_t)SEM_KEY, 2, OBJ_PERMS | IPC_CREAT);
	if(semid == -1)
	{
		printf("semget failed\n");
		exit(EXIT_FAILURE);
	}

	if(InitSemAvilable(semid, WRITE_SEM) == -1)
	{
		printf("InitSemInAvilable failed\n");
		exit(EXIT_FAILURE);
	}
	if(InitSemInUse(semid, READ_SEM) == -1)
	{
		printf("InitSemInUse failed\n");
		exit(EXIT_FAILURE);
	}
	errno = 0;
	shmid = shmget((key_t)SHM_KEY, sizeof(ShmVideo360Struct), OBJ_PERMS | IPC_CREAT);	//创建共享内存
	if(shmid == -1)
	{
		printf("shmget failed\n");
		printf("errno info: %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}
	shm = shmat(shmid, NULL, 0);				//将共享内存连接到当前进程的地址空间
	if(shm == (void*)-1)
	{
		printf("shmat failed\n");
		printf("errno info: %s\n", strerror(errno));
		exit(EXIT_FAILURE);
	}
	printf("Memory attached at %X\n", (int)shm);
	shared360 = (ShmVideo360Struct*)shm;
	shared360->head = 0;
	shared360->len = 0;
	shared360->index = 0;

	while(1)
	{
		if(WaitSem(semid, READ_SEM) == -1) break;

		vbuf = &shared360->data[shared360->head];
		//gettimeofday(&tvl_360, NULL);
		rec_msg.msg_type = MEDIA_VIDEO;
		rec_msg.frame = vbuf;
		rec_msg.used_size = shared360->len;
		rec_msg.channel = MAX_CHANNEL-1;
		rec_msg.ts_sec = tvl_360.tv_sec;
		rec_msg.ts_usec = tvl_360.tv_usec;
		rec_msg.index = shared360->index;
		printf("shm has rcv data len:%d time: %d, index:%d!\n", shared360->len, (unsigned int)tvl_360.tv_sec, shared360->index);
		if(rtmp360_wait_I)
		{
			if(is_iframe(vbuf[4]))
			{
				rtmp360_wait_I = 0;
				printf("rtmp360 wait I frame ok!!!\n");
			}
			else
			{
				rtmp360_wait_I = 1;
			}
		}

		shared360->head += shared360->len;
		PostSem(semid, WRITE_SEM);

		ret = q_send(q_record, (unsigned char *)&rec_msg, sizeof(REC_MSG));
		if((rtmp360 != NULL) && (rtmp_stop360 == 0))
		{
			ret = q_send(q_srs_rtmp360, (unsigned char *)&rec_msg, sizeof(REC_MSG));
		}
	}

	DeleteShm(shmid, shm);
	DeleteSem(semid, 0);
	return 0;
}

谨以此篇记录自己对SystemV信号量以及共享内存的学习,为以后的使用方便查找。

猜你喜欢

转载自blog.csdn.net/zhaoyun_zzz/article/details/82849628