Pipe API

Oasis Pipe는 프로세스간 통신(IPC)를 지원합니다.

oasis 네임스페이스를 사용합니다.

영상이나 오디오 데이터 전송에 특화되어 있습니다. 영상 데이터를 v4l2loopback 장치에 쓸 때 유용합니다.

Oasis Pipe는 다른 Oasis 컴포넌트에 의존적이지 않고, oasis::initialize 없이도 사용가능하므로 Oasis를 사용하지 않는 응용프로그램에서도 liboasis_pipe.so 라이브러리를 링크하며 사용할 수 있습니다.

헤더 파일

OasisPipe.h

형정의

Oasis Pipe가 지원하는 데이터 포맷은 아래와 같습니다. 영상의 경우, V4L2 포맷과 호환됩니다.

enum {
  kOasisPipeDataTypeUserData = 0,
  kOasisPipeDataTypeVideoBGRA,
  kOasisPipeDataTypeVideoARGB,
  kOasisPipeDataTypeVideoYUYV,
  kOasisPipeDataTypeVideoYVYU,
  kOasisPipeDataTypeVideoYUV420,
  kOasisPipeDataTypeVideoYVU420,
  kOasisPipeDataTypeVideoNV12,
  kOasisPipeDataTypeVideoNV21,
  kOasisPipeDataTypeAudioPCM16LE,
  kOasisPipeDataTypeAudioPCM16BE,

  //in case of the following types, width_or_sampling_rate, height_or_channels, stride_or_bits_per_sample are ignored.
  kOasisPipeDataTypeVideoH264,
  kOasisPipeDataTypeVideoH265,
  kOasisPipeDataTypeVideoVP8,
  kOasisPipeDataTypeVideoVP9,
  kOasisPipeDataTypeVideoMJPEG,
};
int32_t pipeDataTypeToV4l2Format ( int32_t data_type )
OasisPipe.h
Oasis 파이프 데이터 타입을 V4L2 포맷 타입으로 변환합니다.
매개변수
data_type  Oasis 파이프 데이터 타입니다.
리턴값
V4L2 포맷 타입니다. 지원하지 않을 경우, -1을 리턴합니다.

아래와 같이 변환됩니다.

From To
kOasisPipeDataTypeVideoBGRA V4L2_PIX_FMT_ABGR32
kOasisPipeDataTypeVideoARGB V4L2_PIX_FMT_ARGB32
kOasisPipeDataTypeVideoYUYV V4L2_PIX_FMT_YUYV
kOasisPipeDataTypeVideoYVYU V4L2_PIX_FMT_YYUV
kOasisPipeDataTypeVideoYUV420 V4L2_PIX_FMT_YUV420
kOasisPipeDataTypeVideoYVU420 V4L2_PIX_FMT_YVU420
kOasisPipeDataTypeVideoNV12 V4L2_PIX_FMT_NV12
kOasisPipeDataTypeVideoNV21 V4L2_PIX_FMT_NV21
kOasisPipeDataTypeVideoH264 V4L2_PIX_FMT_H264
kOasisPipeDataTypeVideoH265 V4L2_PIX_FMT_H265
kOasisPipeDataTypeVideoVP8 V4L2_PIX_FMT_VP8
kOasisPipeDataTypeVideoVP9 V4L2_PIX_FMT_VP9
kOasisPipeDataTypeVideoMJPEG V4L2_PIX_FMT_MJPEG
int32_t v4l2FormatToPipeDataType ( int32_t format )
OasisPipe.h
V4L2 포맷 타입을 Oasis 파이프 데이터 타입으로 변환합니다.
매개변수
format  V4L2 포맷 타입입니다.
리턴값
Oasis 파이프 데이터 타입을 리턴합니다. 지원하지 않을 경우, -1을 리턴합니다.

아래와 같이 변환됩니다.

From To
V4L2_PIX_FMT_BGR32 kOasisPipeDataTypeVideoBGRA
V4L2_PIX_FMT_ABGR32 kOasisPipeDataTypeVideoBGRA
V4L2_PIX_FMT_RGB32 kOasisPipeDataTypeVideoARGB
V4L2_PIX_FMT_ARGB32 kOasisPipeDataTypeVideoARGB
V4L2_PIX_FMT_YUYV kOasisPipeDataTypeVideoYUYV
V4L2_PIX_FMT_YYUV kOasisPipeDataTypeVideoYVYU
V4L2_PIX_FMT_YUV420 kOasisPipeDataTypeVideoYUV420
V4L2_PIX_FMT_YVU420 kOasisPipeDataTypeVideoYVU420
V4L2_PIX_FMT_NV12 kOasisPipeDataTypeVideoNV12
V4L2_PIX_FMT_NV21 kOasisPipeDataTypeVideoNV21
V4L2_PIX_FMT_H264 kOasisPipeDataTypeVideoH264
V4L2_PIX_FMT_H265 kOasisPipeDataTypeVideoH265
V4L2_PIX_FMT_VP8 kOasisPipeDataTypeVideoVP8
V4L2_PIX_FMT_VP9 kOasisPipeDataTypeVideoVP9
V4L2_PIX_FMT_MJPEG kOasisPipeDataTypeVideoMJPEG

Writer

OasisPipeWriterRef pipeWriterOpen ( const char * pipe_path )
OasisPipe.h
파이프 쓰기 객체를 생성합니다.
매개변수
pipe_path  파이프 경로입니다. 경로가 /dev/video 로 시작하면 v4l2loopback 장치로 간주하여 영상 데이터 쓰는 용도로 쓰기 객체를 생성합니다.
리턴값
성공하면 파이프 쓰기 객체를 리턴합니다. 실패하면 nullptr을 리턴합니다.
int32_t pipeWriterClose ( OasisPipeWriterRef & pipe )
OasisPipe.h
파이프 쓰기 객체를 해제합니다.
매개변수
pipe  파이프 쓰기 객체입니다.
리턴값
  • 0: 성공
  • -1: 실패
ssize_t pipeWriterWrite ( const OasisPipeWriterRef & pipe , const void * data , size_t length , int32_t width_or_sampling_rate , int32_t height_or_channels , int32_t stride_or_bits_per_sample , int32_t data_type , uint32_t flags )
OasisPipe.h
파이프에 데이터를 전송합니다.
매개변수
pipe  파이프 쓰기 객체입니다.
data  데이터 포인터입니다.
length  데이터 길이입니다. 바이트 단위입니다.
width_or_sampling_rate  영상 데이터의 경우 너비 값을, 오디오 데이터의 경우 샘플링 레이트를 지정합니다. 사용자 데이터일 경우 임의로 정의하여 사용합니다.
height_or_channels  영상 데이터의 경우 높이 값을, 오디오 데이터의 경우 채널 개수를 지정합니다. 사용자 데이터일 경우 임의로 정의하여 사용합니다.
stride_or_bits_per_sample  영상 데이터의 경우 너비 스트라이드 값을, 오디오 데이터의 경우 샘플 크기를 지정합니다. 사용자 데이터일 경우 임의로 정의하여 사용합니다.
data_type  파이프 데이터 타입을 지정합니다.
flags  플래그 값을 지정합니다. 0값을 입력합니다.
리턴값
성공하면 데이터 길이를 리턴합니다. 실패하면 -1을 리턴하고, 에러 콜백 함수가 등록되었으면 상세 정보가 콜백 함수를 호출하여 전달됩니다.
void pipeWriterSetErrorCallback ( const OasisPipeWriterRef & pipe , std::function<void(int32_t, const void *, size_t, void *)> error_callback , void * user )
OasisPipe.h
쓰는 동안 에러 이벤트가 발생하였을 때 호출되는 콜백 함수를 등록합니다.
매개변수
pipe  파이프 쓰기 객체입니다.
error_callback 
user  콜백 함수에 매개변수로 전달될 사용자 데이터입니다.

에러 이벤트 콜백 함수는 아래와 같습니다.

typedef  void  (*error_callback_) ( int32_t errno , const void * data , size_t length , void * user_data )
OasisPipe.h
매개변수
errno  errno 값입니다.
data  쓰기 호출 때 전달된 데이터 포인터 입니다.
length  쓰기 호출 때 전달된 데이터 길이입니다.
user_data  콜백 등록 시 전달된 사용자 데이터입니다.

Reader

OasisPipeReaderRef pipeReaderOpen ( const char * pipe_path )
OasisPipe.h
파이프 읽기 객체를 생성합니다.
매개변수
pipe_path  파이프 경로입니다.
리턴값
성공하면 파이프 읽기 객체를 리턴합니다. 실패하면 nullptr을 리턴합니다.
void pipeReaderSetCallback ( const OasisPipeReaderRef & pipe , std::function<void(const void *, size_t, int32_t, int32_t, int32_t, int32_t, uint32_t)> callback )
OasisPipe.h
파이프에서 완전한 데이터를 읽었을 때 호출되는 콜백 함수를 등록합니다.
매개변수
pipe  파이프 읽기 객체입니다.
callback  읽기 콜백 함수를 등록합니다.

읽기 콜백 함수 형정의는 아래와 같습니다.

typedef  void  (*callback) ( const void * data , size_t size , int32_t width_or_sampling_rate , int32_t height_or_channels , int32_t stride_or_bits_per_sample , int32_t data_type , uint32_t flags )
OasisPipe.h
매개변수
data  데이터가 저장된 버퍼 포인터입니다.
size  데이터 길이입니다. 바이트 단위입니다.
width_or_sampling_rate  영상 데이터의 경우 너비 값을, 오디오 데이터의 경우 샘플링 레이트를 의미합니다. 사용자 데이터일 경우 임의로 정의하여 사용합니다.
height_or_channels  영상 데이터의 경우 높이 값을, 오디오 데이터의 경우 채널 개수를 의미합니다. 사용자 데이터일 경우 임의로 정의하여 사용합니다.
stride_or_bits_per_sample  영상 데이터의 경우 너비 스트라이드 값을, 오디오 데이터의 경우 샘플 크기를 의미합니다. 사용자 데이터일 경우 임의로 정의하여 사용합니다.
data_type  데이터 타입입니다.
flags  플래그 값입니다.
int32_t pipeReaderClose ( OasisPipeReaderRef & pipe )
OasisPipe.h
파이프 읽기 객체를 해제합니다.
매개변수
pipe  파이프 읽기 객체입니다.
리턴값
  • 0: 성공
  • -1: 실패

예제

아래는 한 프로세스에서 다른 프로세스에 OFFS 포맷을 파이프로 요청하고 그 응답을 파이프로 받는 예입니다.

JSON 처리를 위해 JsonCpp 라이브러리를 사용합니다.

명령 요청 프로세스

읽기 쓰기 파이프를 생성하고 각각 콜백을 등록합니다.

OasisPipeReaderRef pipe_reader_ = pipeReaderOpen("/tmp/oasis-mon-res");
pipeReaderSetCallback(pipe_reader_, std::bind(&onPipeCommandCompleted, _1, _2, _3, _4, _5, _6, _7));

OasisPipeWriterRef pipe_writer_ = pipeWriterOpen("/tmp/oasis-mon-req");
pipeWriterSetErrorCallback(pipe_writer_, std::bind(&onPipeCommandWriteError, _1, _2, _3, _4), nullptr);

읽기 콜백 함수입니다.

void onPipeCommandCompleted(const void *data, size_t length, int32_t width, int32_t height, int32_t stride, int32_t data_type, uint32_t flags)
{
  std::string reply((const char*)data, (const char*)data+length);

  Json::Value json_reply;
  Json::Reader reader;
  bool parsing_ok = reader.parse(reply.c_str(), json_reply);
  if(!parsing_ok) {
    return;
  }

  if(json_reply.isMember("result")) {
    //결과를 확인합니다.
    int32_t result = std::stoi(json_reply["result"].asString());
  }
}

쓰기 오류 이벤트 콜백 함수입니다.

void DashApp::onPipeCommandWriteError(int32_t error, const void *data, size_t length, void *user)
{
}

명령 수행 프로세스에 OFFS format 명령을 전달합니다.

Json::Value json_req;
json_req["cmd"] = "format";

Json::FastWriter fastWriter;
std::string json_string = fastWriter.write(json_req);

pipeWriterWrite(pipe_writer_, json_string.c_str(), json_string.length(), 0, 0, 0, 0, 0);

명령 수행 프로세스

읽기 쓰기 파이프를 생성하고 각각 콜백을 등록합니다. 명령 요청 프로세스에서 사용하는 경로가 각각 바뀌어서 사용됩니다.

// 명령을 수행하고 결과를 명령 요청 프로세스에게 전달합니다.
OasisPipeWriterRef writer = pipeWriterOpen("/tmp/oasis-mon-res");

// 명령 요청 프로세스로 부터 명령 요청 메세지를 읽습니다.
OasisPipeReaderRef reader = pipeReaderOpen("/tmp/oasis-mon-req");
pipeReaderSetCallback(reader, std::bind(&onCommand, _1, _2, _3, _4, _5, _6, _7));

명령 읽는 콜백 함수입니다.

static void onCommand(const void *data, size_t length, int32_t width, int32_t height, int32_t stride, int32_t data_type, uint32_t flags)
{
  if(length == 0) {
    //nothing to do
    return;
  }

  std::string cmd((const char *)data, (const char *)data+length);

  Json::Value json_req;
  Json::Reader reader;
  bool parsing_ok = reader.parse(cmd.c_str(), json_req);
  if(!parsing_ok) {
    fprintf(stdout, "pipe req: json parsing error: %s\n", cmd.c_str());
    continue;
  }

  if(json_req.isMember("cmd") == false) {
    fprintf(stdout, "pipe req: \"cmd\" not found!: %s\n", cmd.c_str());
    continue;
  }

  std::string req_cmd = json_req["cmd"].asString();
  if(req_cmd == "format") {
    //파일 시스템 포맷을 진행합니다.
    char shell_cmd[PATH_MAX];
    int32_t req_cmd_result = 0;
    sprintf(shell_cmd, "/usr/sbin/mkfs.offs /dev/mmcblk0p1 /etc/offs-format.cfg 32GB_DRV");
    err = runCommand(shell_cmd);
    if(err) {
      req_cmd_result = -err;
    }

    Json::Value json_reply;

    json_reply["result"] = std::to_string(req_cmd_result);
    json_reply["cmd"] = req_cmd;

    Json::FastWriter fastWriter;
    std::string json_string = fastWriter.write(json_reply);

    pipeWriterWrite(writer, json_string.c_str(), json_string.length(), 0, 0, 0, 0, 0);
  }
}

popen 함수를 이용하여 명령을 수행하는 함수입니다.

#define MAXLINE 4096

static int32_t runCommand(const char *shell_cmd)
{
    char buff[MAXLINE];
    FILE *fp;

    fp = popen(shell_cmd, "r");
    if(fp == NULL) {
        fprintf(stderr, "\"%s\" popen error: %d(%s)\n", shell_cmd, errno, strerror(errno));
        return -errno;
    }

    while(fgets(buff, MAXLINE, fp) != NULL) {
        fprintf(stdout, "%s", buff);
        fflush(stdout);
    }

    return (int32_t)(int8_t)pclose(fp);
}