前言
最近在做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]);