cosmopolitan/dsp/mpeg/ycbcrio.c

140 lines
5.8 KiB
C
Raw Normal View History

2020-06-15 14:18:57 +00:00
/*-*- 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));
}