140 lines
5.8 KiB
C
140 lines
5.8 KiB
C
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||
|
│ │
|
||
|
│ This program is free software; you can redistribute it and/or modify │
|
||
|
│ it under the terms of the GNU General Public License as published by │
|
||
|
│ the Free Software Foundation; version 2 of the License. │
|
||
|
│ │
|
||
|
│ This program is distributed in the hope that it will be useful, but │
|
||
|
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
|
||
|
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
|
||
|
│ General Public License for more details. │
|
||
|
│ │
|
||
|
│ You should have received a copy of the GNU General Public License │
|
||
|
│ along with this program; if not, write to the Free Software │
|
||
|
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||
|
│ 02110-1301 USA │
|
||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||
|
#include "dsp/mpeg/mpeg.h"
|
||
|
#include "dsp/mpeg/ycbcrio.h"
|
||
|
#include "libc/bits/bits.h"
|
||
|
#include "libc/calls/calls.h"
|
||
|
#include "libc/calls/struct/stat.h"
|
||
|
#include "libc/log/check.h"
|
||
|
#include "libc/macros.h"
|
||
|
#include "libc/runtime/runtime.h"
|
||
|
#include "libc/str/str.h"
|
||
|
#include "libc/sysv/consts/map.h"
|
||
|
#include "libc/sysv/consts/o.h"
|
||
|
#include "libc/sysv/consts/prot.h"
|
||
|
|
||
|
static void CheckPlmFrame(const struct plm_frame_t *frame) {
|
||
|
CHECK_NE(0, frame->width);
|
||
|
CHECK_NE(0, frame->height);
|
||
|
CHECK_GE(frame->y.width, frame->width);
|
||
|
CHECK_GE(frame->y.height, frame->height);
|
||
|
CHECK_EQ(frame->cr.width, frame->cb.width);
|
||
|
CHECK_EQ(frame->cr.height, frame->cb.height);
|
||
|
CHECK_EQ(frame->y.width, frame->cr.width * 2);
|
||
|
CHECK_EQ(frame->y.height, frame->cr.height * 2);
|
||
|
CHECK_NOTNULL(frame->y.data);
|
||
|
CHECK_NOTNULL(frame->cr.data);
|
||
|
CHECK_NOTNULL(frame->cb.data);
|
||
|
}
|
||
|
|
||
|
static size_t GetHeaderBytes(const struct plm_frame_t *frame) {
|
||
|
return MAX(sizeof(struct Ycbcrio), ROUNDUP(frame->y.width, 16));
|
||
|
}
|
||
|
|
||
|
static size_t GetPlaneBytes(const struct plm_plane_t *plane) {
|
||
|
/*
|
||
|
* planes must be 16-byte aligned, but due to their hugeness, and the
|
||
|
* recommendation of intel's 6,000 page manual, it makes sense to have
|
||
|
* planes on isolated 64kb frames for multiprocessing.
|
||
|
*/
|
||
|
return ROUNDUP(ROUNDUP(plane->height, 16) * ROUNDUP(plane->width, 16),
|
||
|
FRAMESIZE);
|
||
|
}
|
||
|
|
||
|
static size_t CalcMapBytes(const struct plm_frame_t *frame) {
|
||
|
return ROUNDUP(GetHeaderBytes(frame) + GetPlaneBytes(&frame->y) +
|
||
|
GetPlaneBytes(&frame->cb) + GetPlaneBytes(&frame->cb),
|
||
|
FRAMESIZE);
|
||
|
}
|
||
|
|
||
|
static void FixupPointers(struct Ycbcrio *map) {
|
||
|
map->frame.y.data = (unsigned char *)map + GetHeaderBytes(&map->frame);
|
||
|
map->frame.cr.data = map->frame.y.data + GetPlaneBytes(&map->frame.y);
|
||
|
map->frame.cb.data = map->frame.cr.data + GetPlaneBytes(&map->frame.cr);
|
||
|
}
|
||
|
|
||
|
static struct Ycbcrio *YcbcrioOpenNew(const char *path,
|
||
|
const struct plm_frame_t *frame) {
|
||
|
int fd;
|
||
|
size_t size;
|
||
|
struct stat st;
|
||
|
struct Ycbcrio *map;
|
||
|
CheckPlmFrame(frame);
|
||
|
size = CalcMapBytes(frame);
|
||
|
CHECK_NE(-1, (fd = open(path, O_CREAT | O_RDWR, 0644)));
|
||
|
CHECK_NE(-1, fstat(fd, &st));
|
||
|
if (st.st_size < size) {
|
||
|
CHECK_NE(-1, ftruncate(fd, size));
|
||
|
}
|
||
|
CHECK_NE(MAP_FAILED,
|
||
|
(map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)));
|
||
|
map->magic = YCBCRIO_MAGIC;
|
||
|
map->fd = fd;
|
||
|
map->size = size;
|
||
|
memcpy(&map->frame, frame, sizeof(map->frame));
|
||
|
FixupPointers(map);
|
||
|
memcpy(&map->frame.y.data, frame->y.data, GetPlaneBytes(&frame->y));
|
||
|
memcpy(&map->frame.cb.data, frame->cb.data, GetPlaneBytes(&frame->cb));
|
||
|
memcpy(&map->frame.cr.data, frame->cr.data, GetPlaneBytes(&frame->cr));
|
||
|
return map;
|
||
|
}
|
||
|
|
||
|
static struct Ycbcrio *YcbcrioOpenExisting(const char *path) {
|
||
|
int fd;
|
||
|
struct stat st;
|
||
|
struct Ycbcrio *map;
|
||
|
CHECK_NE(-1, (fd = open(path, O_RDWR)));
|
||
|
CHECK_NE(-1, fstat(fd, &st));
|
||
|
CHECK_NE(MAP_FAILED, (map = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE,
|
||
|
MAP_SHARED, fd, 0)));
|
||
|
CHECK_EQ(YCBCRIO_MAGIC, map->magic);
|
||
|
CHECK_GE(st.st_size, CalcMapBytes(&map->frame));
|
||
|
FixupPointers(map);
|
||
|
map->fd = fd;
|
||
|
map->size = st.st_size;
|
||
|
return map;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Opens shareable persistable MPEG video frame memory.
|
||
|
*
|
||
|
* @param path is a file name
|
||
|
* @param frame if NULL means open existing file, otherwise copies new
|
||
|
* @param points to pointer returned by YcbcrioOpen() which is cleared
|
||
|
* @return memory mapping needing YcbcrioClose()
|
||
|
*/
|
||
|
struct Ycbcrio *YcbcrioOpen(const char *path, const struct plm_frame_t *frame) {
|
||
|
if (frame) {
|
||
|
return YcbcrioOpenNew(path, frame);
|
||
|
} else {
|
||
|
return YcbcrioOpenExisting(path);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Closes mapped video frame file.
|
||
|
*
|
||
|
* @param points to pointer returned by YcbcrioOpen() which is cleared
|
||
|
*/
|
||
|
void YcbcrioClose(struct Ycbcrio **map) {
|
||
|
CHECK_NE(-1, close_s(&(*map)->fd));
|
||
|
CHECK_NE(-1, munmap_s(map, (*map)->size));
|
||
|
}
|