Compare commits
17 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
54bc22532a | 4 years ago |
![]() |
f47750131a | 4 years ago |
![]() |
44a194886d | 4 years ago |
![]() |
ac56f5566b | 4 years ago |
![]() |
b09e1b907f | 4 years ago |
![]() |
f8c9983d9b | 4 years ago |
![]() |
c768e70fba | 4 years ago |
![]() |
8b1c0a3bb7 | 4 years ago |
![]() |
f4826ebd20 | 4 years ago |
![]() |
a99b76286c | 4 years ago |
![]() |
c54b705930 | 4 years ago |
![]() |
777b5c57de | 4 years ago |
![]() |
4d7eb8976b | 4 years ago |
![]() |
a930a0cded | 4 years ago |
![]() |
ccae6d174b | 4 years ago |
![]() |
21cc797dd3 | 4 years ago |
![]() |
c961c56c39 | 4 years ago |
@ -1,4 +1,4 @@
|
||||
[submodule "kasmweb"]
|
||||
path = kasmweb
|
||||
url = https://github.com/kasmtech/noVNC.git
|
||||
branch = master
|
||||
branch = video
|
||||
|
@ -0,0 +1,507 @@
|
||||
/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
|
||||
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
|
||||
* Copyright 2014 Pierre Ossman for Cendio AB
|
||||
*
|
||||
* This 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
#include <rdr/OutStream.h>
|
||||
#include <rfb/EncCache.h>
|
||||
#include <rfb/encodings.h>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <rfb/SConnection.h>
|
||||
#include <rfb/ServerCore.h>
|
||||
#include <rfb/PixelBuffer.h>
|
||||
#include <rfb/TightX264Encoder.h>
|
||||
#include <rfb/TightConstants.h>
|
||||
|
||||
#include <webp/encode.h>
|
||||
#include <x264.h>
|
||||
#include "nvidia.h"
|
||||
#include "mp4.h"
|
||||
|
||||
#define MAX_FRAMELEN (1024 * 1024)
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
static LogWriter vlog("x264");
|
||||
|
||||
static const PixelFormat pfRGBX(32, 24, false, true, 255, 255, 255, 0, 8, 16);
|
||||
static const PixelFormat pfBGRX(32, 24, false, true, 255, 255, 255, 16, 8, 0);
|
||||
|
||||
bool TightX264Encoder::skip_nvidia = false;
|
||||
|
||||
TightX264Encoder::TightX264Encoder(SConnection* conn, EncCache *cache_, uint8_t cacheType_) :
|
||||
Encoder(conn, encodingTight, (EncoderFlags)(EncoderUseNativePF | EncoderLossy), -1),
|
||||
keyframe(true), enc(NULL), params(NULL), mux(NULL), muxstate(NULL), framectr(0),
|
||||
nvidia_init_done(false), using_nvidia(true),
|
||||
encCache(cache_), cacheType(cacheType_),
|
||||
framebuf(NULL), framelen(0), bitbuf(NULL), myw(0), myh(0)
|
||||
{
|
||||
params = new x264_param_t;
|
||||
x264_param_default_preset(params, "veryfast", "zerolatency");
|
||||
|
||||
params->i_threads = X264_THREADS_AUTO;
|
||||
params->i_fps_num = params->i_keyint_max = rfb::Server::frameRate;
|
||||
params->i_fps_den = 1;
|
||||
params->rc.i_rc_method = X264_RC_ABR;
|
||||
params->rc.i_bitrate = rfb::Server::x264Bitrate;
|
||||
params->i_csp = X264_CSP_I420;
|
||||
params->i_log_level = X264_LOG_WARNING;
|
||||
params->b_annexb = 0;
|
||||
|
||||
framebuf = new uint8_t[MAX_FRAMELEN];
|
||||
bitbuf = new uint8_t[MAX_FRAMELEN];
|
||||
mux = new Mp4Context;
|
||||
memset(mux, 0, sizeof(Mp4Context));
|
||||
muxstate = new Mp4State;
|
||||
memset(muxstate, 0, sizeof(Mp4State));
|
||||
}
|
||||
|
||||
TightX264Encoder::~TightX264Encoder()
|
||||
{
|
||||
delete params;
|
||||
delete [] framebuf;
|
||||
delete [] bitbuf;
|
||||
delete mux;
|
||||
delete muxstate;
|
||||
}
|
||||
|
||||
bool TightX264Encoder::isSupported()
|
||||
{
|
||||
if (!conn->cp.supportsEncoding(encodingTight))
|
||||
return false;
|
||||
|
||||
// Unconditional support if enabled
|
||||
return rfb::Server::x264Bitrate != 0;
|
||||
}
|
||||
|
||||
void TightX264Encoder::mp4_write_callback(const void *buffer, size_t size)
|
||||
{
|
||||
if (framelen + size > MAX_FRAMELEN)
|
||||
vlog.error("Tried to write too large a frame, %lu bytes", framelen + size);
|
||||
|
||||
memcpy(&framebuf[framelen], buffer, size);
|
||||
framelen += size;
|
||||
}
|
||||
|
||||
void TightX264Encoder::writeRect(const PixelBuffer* pb, const Palette& palette)
|
||||
{
|
||||
const rdr::U8* buffer;
|
||||
int stride;
|
||||
|
||||
rdr::OutStream* os;
|
||||
|
||||
if (pb->width() < 320)
|
||||
return; // Sometimes we get sent an 1x1 frame, or a cursor
|
||||
|
||||
uint32_t w, h;
|
||||
w = pb->width();
|
||||
h = pb->height();
|
||||
|
||||
os = conn->getOutStream();
|
||||
|
||||
if (using_nvidia) {
|
||||
|
||||
if (w != myw || h != myh) {
|
||||
if (nvidia_init_done)
|
||||
nvidia_unload();
|
||||
nvidia_init_done = false;
|
||||
}
|
||||
|
||||
if (!nvidia_init_done) {
|
||||
if (nvidia_init(w, h, rfb::Server::x264Bitrate,
|
||||
rfb::Server::frameRate) != 0) {
|
||||
vlog.error("nvidia init failed, disabling h264");
|
||||
rfb::Server::x264Bitrate.setParam(0);
|
||||
return;
|
||||
}
|
||||
nvidia_init_done = true;
|
||||
myw = w;
|
||||
myh = h;
|
||||
}
|
||||
|
||||
uint32_t cachelen;
|
||||
const void *cachedata;
|
||||
if (encCache->enabled &&
|
||||
(cachedata = encCache->get(cacheType, framectr, 0, w, h, cachelen))) {
|
||||
os->writeU8(tightX264 << 4);
|
||||
writeCompact(cachelen, os);
|
||||
os->writeBytes(cachedata, cachelen);
|
||||
framectr++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (keyframe) {
|
||||
framectr = 0;
|
||||
keyframe = false;
|
||||
|
||||
free(mux->buf_header.buf);
|
||||
free(mux->buf_mdat.buf);
|
||||
free(mux->buf_moof.buf);
|
||||
memset(mux, 0, sizeof(Mp4Context));
|
||||
memset(muxstate, 0, sizeof(Mp4State));
|
||||
}
|
||||
|
||||
mux->framerate = rfb::Server::frameRate;
|
||||
mux->w = w;
|
||||
mux->h = h;
|
||||
|
||||
buffer = pb->getBuffer(pb->getRect(), &stride);
|
||||
|
||||
if (!pfBGRX.equal(pb->getPF())) {
|
||||
vlog.error("unsupported pixel format");
|
||||
return;
|
||||
}
|
||||
|
||||
// Encode
|
||||
uint32_t bitlen;
|
||||
if (nvenc_frame(buffer, framectr++, bitbuf, bitlen) != 0) {
|
||||
vlog.error("encoding failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Need to parse NALs out of the stream
|
||||
const uint8_t prefix[3] = { 0, 0, 1 };
|
||||
const uint8_t *nalptr = bitbuf;
|
||||
int i_nals = 0;
|
||||
const uint8_t *nalstarts[32] = { NULL };
|
||||
uint32_t nallens[32] = { 0 };
|
||||
uint32_t remlen = bitlen;
|
||||
|
||||
while (1) {
|
||||
const uint8_t *next = (uint8_t *) memmem(nalptr, remlen, prefix, 3);
|
||||
if (!next)
|
||||
break;
|
||||
|
||||
remlen -= (next + 3) - nalptr;
|
||||
nalptr = nalstarts[i_nals] = next + 3;
|
||||
|
||||
i_nals++;
|
||||
};
|
||||
|
||||
// Lens
|
||||
int i;
|
||||
for (i = 0; i < i_nals; i++) {
|
||||
if (i == i_nals - 1) {
|
||||
nallens[i] = bitbuf + bitlen - nalstarts[i];
|
||||
} else {
|
||||
nallens[i] = nalstarts[i + 1] - nalstarts[i] - 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Mux
|
||||
framelen = 0;
|
||||
os->writeU8(tightX264 << 4);
|
||||
|
||||
for (i = 0; i < i_nals; i++) {
|
||||
uint32_t pack_len = nallens[i];
|
||||
const uint8_t *pack_data = nalstarts[i];
|
||||
|
||||
struct NAL nal; nal_parse_header(&nal, pack_data[0]);
|
||||
|
||||
switch (nal.unit_type) {
|
||||
case NalUnitType_SPS: { set_sps(mux, pack_data, pack_len); break; }
|
||||
case NalUnitType_PPS: { set_pps(mux, pack_data, pack_len); break; }
|
||||
case NalUnitType_CodedSliceIdr:
|
||||
case NalUnitType_CodedSliceNonIdr: {
|
||||
// Write all remaining NALs under the assumption they are the same type.
|
||||
const uint32_t origlen = pack_len;
|
||||
pack_len = bitbuf + bitlen - pack_data;
|
||||
set_slice(mux, pack_data, origlen, pack_len, nal.unit_type);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (nal.unit_type != NalUnitType_CodedSliceIdr &&
|
||||
nal.unit_type != NalUnitType_CodedSliceNonIdr)
|
||||
continue;
|
||||
|
||||
enum BufError err;
|
||||
if (!muxstate->header_sent) {
|
||||
struct BitBuf header_buf;
|
||||
err = get_header(mux, &header_buf); chk_err_continue
|
||||
mp4_write_callback(header_buf.buf, header_buf.offset);
|
||||
|
||||
muxstate->sequence_number = 1;
|
||||
muxstate->base_data_offset = header_buf.offset;
|
||||
muxstate->base_media_decode_time = 0;
|
||||
muxstate->header_sent = true;
|
||||
muxstate->nals_count = 0;
|
||||
muxstate->default_sample_duration = default_sample_size;
|
||||
}
|
||||
|
||||
err = set_mp4_state(mux, muxstate); chk_err_continue
|
||||
{
|
||||
struct BitBuf moof_buf;
|
||||
err = get_moof(mux, &moof_buf); chk_err_continue
|
||||
mp4_write_callback(moof_buf.buf, moof_buf.offset);
|
||||
}
|
||||
{
|
||||
struct BitBuf mdat_buf;
|
||||
err = get_mdat(mux, &mdat_buf); chk_err_continue
|
||||
mp4_write_callback(mdat_buf.buf, mdat_buf.offset);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (encCache->enabled) {
|
||||
void *tmp = malloc(framelen);
|
||||
memcpy(tmp, framebuf, framelen);
|
||||
encCache->add(cacheType, framectr, 0, w, h, framelen, tmp);
|
||||
}
|
||||
|
||||
writeCompact(framelen, os);
|
||||
os->writeBytes(framebuf, framelen);
|
||||
} else {
|
||||
w += w & 1;
|
||||
h += h & 1;
|
||||
|
||||
params->i_width = w;
|
||||
params->i_height = h;
|
||||
|
||||
x264_param_apply_profile(params, "baseline");
|
||||
|
||||
uint32_t cachelen;
|
||||
const void *cachedata;
|
||||
if (encCache->enabled &&
|
||||
(cachedata = encCache->get(cacheType, framectr, 0, w, h, cachelen))) {
|
||||
os->writeU8(tightX264 << 4);
|
||||
writeCompact(cachelen, os);
|
||||
os->writeBytes(cachedata, cachelen);
|
||||
framectr++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (keyframe) {
|
||||
framectr = 0;
|
||||
keyframe = false;
|
||||
|
||||
free(mux->buf_header.buf);
|
||||
free(mux->buf_mdat.buf);
|
||||
free(mux->buf_moof.buf);
|
||||
memset(mux, 0, sizeof(Mp4Context));
|
||||
memset(muxstate, 0, sizeof(Mp4State));
|
||||
}
|
||||
|
||||
mux->framerate = rfb::Server::frameRate;
|
||||
mux->w = params->i_width;
|
||||
mux->h = params->i_height;
|
||||
|
||||
if (!enc) {
|
||||
enc = x264_encoder_open(params);
|
||||
}
|
||||
|
||||
buffer = pb->getBuffer(pb->getRect(), &stride);
|
||||
|
||||
// Convert it to yuv420 using libwebp's helper functions
|
||||
WebPPicture pic;
|
||||
|
||||
WebPPictureInit(&pic);
|
||||
pic.width = pb->getRect().width();
|
||||
pic.height = pb->getRect().height();
|
||||
|
||||
bool freebuffer = false;
|
||||
if (pic.width & 1 || pic.height & 1) {
|
||||
// Expand to divisible-by-2 for x264
|
||||
freebuffer = true;
|
||||
const uint32_t oldw = pic.width;
|
||||
const uint32_t oldh = pic.height;
|
||||
pic.width += pic.width & 1;
|
||||
pic.height += pic.height & 1;
|
||||
stride = pic.width;
|
||||
const rdr::U8 *oldbuffer = buffer;
|
||||
buffer = (const rdr::U8*) calloc(pic.width * pic.height, 4);
|
||||
|
||||
uint32_t y;
|
||||
for (y = 0; y < oldh; y++)
|
||||
memcpy((void *) &buffer[y * stride * 4], &oldbuffer[y * oldw * 4], oldw * 4);
|
||||
}
|
||||
|
||||
if (pfRGBX.equal(pb->getPF())) {
|
||||
WebPPictureImportRGBX(&pic, buffer, stride * 4);
|
||||
} else if (pfBGRX.equal(pb->getPF())) {
|
||||
WebPPictureImportBGRX(&pic, buffer, stride * 4);
|
||||
} else {
|
||||
rdr::U8* tmpbuf = new rdr::U8[pic.width * pic.height * 3];
|
||||
pb->getPF().rgbFromBuffer(tmpbuf, (const rdr::U8 *) buffer, pic.width, stride, pic.height);
|
||||
stride = pic.width * 3;
|
||||
|
||||
WebPPictureImportRGB(&pic, tmpbuf, stride);
|
||||
delete [] tmpbuf;
|
||||
}
|
||||
|
||||
if (freebuffer)
|
||||
free((void *) buffer);
|
||||
|
||||
// Wrap
|
||||
x264_picture_t pic_in, pic_out;
|
||||
x264_picture_init(&pic_in);
|
||||
|
||||
pic_in.img.i_csp = X264_CSP_I420;
|
||||
pic_in.img.i_plane = 3;
|
||||
|
||||
pic_in.img.plane[0] = pic.y;
|
||||
pic_in.img.plane[1] = pic.u;
|
||||
pic_in.img.plane[2] = pic.v;
|
||||
|
||||
pic_in.img.i_stride[0] = pic.y_stride;
|
||||
pic_in.img.i_stride[1] = pic_in.img.i_stride[2] = pic.uv_stride;
|
||||
|
||||
pic_in.i_pts = framectr++;
|
||||
|
||||
// Encode
|
||||
int i_nals;
|
||||
x264_nal_t *nals;
|
||||
const int len = x264_encoder_encode(enc, &nals, &i_nals, &pic_in, &pic_out);
|
||||
|
||||
if (len <= 0 || i_nals <= 0)
|
||||
vlog.info("encoding error");
|
||||
|
||||
// Mux
|
||||
framelen = 0;
|
||||
os->writeU8(tightX264 << 4);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < i_nals; i++) {
|
||||
uint32_t pack_len = nals[i].i_payload - 4;
|
||||
const uint8_t *pack_data = nals[i].p_payload;
|
||||
|
||||
pack_data += 4; // Skip size
|
||||
|
||||
struct NAL nal; nal_parse_header(&nal, pack_data[0]);
|
||||
|
||||
switch (nal.unit_type) {
|
||||
case NalUnitType_SPS: { set_sps(mux, pack_data, pack_len); break; }
|
||||
case NalUnitType_PPS: { set_pps(mux, pack_data, pack_len); break; }
|
||||
case NalUnitType_CodedSliceIdr:
|
||||
case NalUnitType_CodedSliceNonIdr: {
|
||||
// Write all remaining NALs under the assumption they are the same type.
|
||||
const uint32_t origlen = pack_len;
|
||||
int j;
|
||||
for (j = i + 1; j < i_nals; j++)
|
||||
pack_len += nals[j].i_payload;
|
||||
set_slice(mux, pack_data, origlen, pack_len, nal.unit_type);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (nal.unit_type != NalUnitType_CodedSliceIdr &&
|
||||
nal.unit_type != NalUnitType_CodedSliceNonIdr)
|
||||
continue;
|
||||
|
||||
enum BufError err;
|
||||
if (!muxstate->header_sent) {
|
||||
struct BitBuf header_buf;
|
||||
err = get_header(mux, &header_buf); chk_err_continue
|
||||
mp4_write_callback(header_buf.buf, header_buf.offset);
|
||||
|
||||
muxstate->sequence_number = 1;
|
||||
muxstate->base_data_offset = header_buf.offset;
|
||||
muxstate->base_media_decode_time = 0;
|
||||
muxstate->header_sent = true;
|
||||
muxstate->nals_count = 0;
|
||||
muxstate->default_sample_duration = default_sample_size;
|
||||
}
|
||||
|
||||
err = set_mp4_state(mux, muxstate); chk_err_continue
|
||||
{
|
||||
struct BitBuf moof_buf;
|
||||
err = get_moof(mux, &moof_buf); chk_err_continue
|
||||
mp4_write_callback(moof_buf.buf, moof_buf.offset);
|
||||
}
|
||||
{
|
||||
struct BitBuf mdat_buf;
|
||||
err = get_mdat(mux, &mdat_buf); chk_err_continue
|
||||
mp4_write_callback(mdat_buf.buf, mdat_buf.offset);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (encCache->enabled) {
|
||||
void *tmp = malloc(framelen);
|
||||
memcpy(tmp, framebuf, framelen);
|
||||
encCache->add(cacheType, framectr, 0, w, h, framelen, tmp);
|
||||
}
|
||||
|
||||
writeCompact(framelen, os);
|
||||
os->writeBytes(framebuf, framelen);
|
||||
|
||||
// Cleanup
|
||||
WebPPictureFree(&pic);
|
||||
x264_encoder_close(enc);
|
||||
enc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void TightX264Encoder::writeSolidRect(int width, int height,
|
||||
const PixelFormat& pf,
|
||||
const rdr::U8* colour)
|
||||
{
|
||||
// FIXME: Add a shortcut in the X264 compressor to handle this case
|
||||
// without having to use the default fallback which is very slow.
|
||||
Encoder::writeSolidRect(width, height, pf, colour);
|
||||
}
|
||||
|
||||
void TightX264Encoder::writeCompact(rdr::U32 value, rdr::OutStream* os) const
|
||||
{
|
||||
// Copied from TightEncoder as it's overkill to inherit just for this
|
||||
rdr::U8 b;
|
||||
|
||||
b = value & 0x7F;
|
||||
if (value <= 0x7F) {
|
||||
os->writeU8(b);
|
||||
} else {
|
||||
os->writeU8(b | 0x80);
|
||||
b = value >> 7 & 0x7F;
|
||||
if (value <= 0x3FFF) {
|
||||
os->writeU8(b);
|
||||
} else {
|
||||
os->writeU8(b | 0x80);
|
||||
os->writeU8(value >> 14 & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TightX264Encoder::tryInit(const PixelBuffer* pb) {
|
||||
if (nvidia_init_done)
|
||||
return true;
|
||||
|
||||
uint32_t w, h;
|
||||
w = pb->width();
|
||||
h = pb->height();
|
||||
|
||||
if (skip_nvidia || nvidia_init(w, h, rfb::Server::x264Bitrate,
|
||||
rfb::Server::frameRate) != 0) {
|
||||
if (!skip_nvidia)
|
||||
vlog.error("nvidia init failed, falling back to x264");
|
||||
using_nvidia = false;
|
||||
nvidia_init_done = true;
|
||||
skip_nvidia = true;
|
||||
myw = w;
|
||||
myh = h;
|
||||
return true;
|
||||
}
|
||||
|
||||
nvidia_init_done = true;
|
||||
myw = w;
|
||||
myh = h;
|
||||
|
||||
return true;
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
|
||||
* Copyright (C) 2011 D. R. Commander
|
||||
* Copyright 2014 Pierre Ossman for Cendio AB
|
||||
*
|
||||
* This 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software 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 software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
#ifndef __RFB_TIGHTX264ENCODER_H__
|
||||
#define __RFB_TIGHTX264ENCODER_H__
|
||||
|
||||
#include <rfb/Encoder.h>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
struct x264_t;
|
||||
struct x264_param_t;
|
||||
struct Mp4Context;
|
||||
struct Mp4State;
|
||||
|
||||
namespace rfb {
|
||||
|
||||
class EncCache;
|
||||
|
||||
class TightX264Encoder : public Encoder {
|
||||
public:
|
||||
TightX264Encoder(SConnection* conn, EncCache *encCache, uint8_t cacheType);
|
||||
virtual ~TightX264Encoder();
|
||||
|
||||
virtual bool isSupported();
|
||||
|
||||
virtual void setQualityLevel(int level) {}
|
||||
virtual void setFineQualityLevel(int quality, int subsampling) {}
|
||||
|
||||
virtual void writeRect(const PixelBuffer* pb, const Palette& palette);
|
||||
virtual void writeSolidRect(int width, int height,
|
||||
const PixelFormat& pf,
|
||||
const rdr::U8* colour);
|
||||
|
||||
virtual void setKeyframe() { keyframe = true; }
|
||||
|
||||
bool tryInit(const PixelBuffer* pb);
|
||||
|
||||
protected:
|
||||
void writeCompact(rdr::U32 value, rdr::OutStream* os) const;
|
||||
void mp4_write_callback(const void *buffer, size_t size);
|
||||
|
||||
protected:
|
||||
bool keyframe;
|
||||
x264_t *enc;
|
||||
x264_param_t *params;
|
||||
Mp4Context *mux;
|
||||
Mp4State *muxstate;
|
||||
unsigned framectr;
|
||||
|
||||
bool nvidia_init_done;
|
||||
bool using_nvidia;
|
||||
static bool skip_nvidia;
|
||||
|
||||
EncCache *encCache;
|
||||
uint8_t cacheType;
|
||||
public:
|
||||
uint8_t *framebuf;
|
||||
uint32_t framelen;
|
||||
|
||||
uint8_t *bitbuf;
|
||||
|
||||
uint16_t myw, myh;
|
||||
};
|
||||
}
|
||||
#endif
|
@ -0,0 +1,423 @@
|
||||
/*
|
||||
* This copyright notice applies to this header file only:
|
||||
*
|
||||
* Copyright (c) 2016
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the software, and to permit persons to whom the
|
||||
* software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#if !defined(FFNV_DYNLINK_CUDA_H) && !defined(CUDA_VERSION)
|
||||
#define FFNV_DYNLINK_CUDA_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define CUDA_VERSION 7050
|
||||
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
#define CUDAAPI __stdcall
|
||||
#else
|
||||
#define CUDAAPI
|
||||
#endif
|
||||
|
||||
#define CU_CTX_SCHED_BLOCKING_SYNC 4
|
||||
|
||||
typedef int CUdevice;
|
||||
#if defined(__x86_64) || defined(AMD64) || defined(_M_AMD64) || defined(__LP64__) || defined(__aarch64__)
|
||||
typedef unsigned long long CUdeviceptr;
|
||||
#else
|
||||
typedef unsigned int CUdeviceptr;
|
||||
#endif
|
||||
typedef unsigned long long CUtexObject;
|
||||
|
||||
typedef struct CUarray_st *CUarray;
|
||||
typedef struct CUctx_st *CUcontext;
|
||||
typedef struct CUstream_st *CUstream;
|
||||
typedef struct CUevent_st *CUevent;
|
||||
typedef struct CUfunc_st *CUfunction;
|
||||
typedef struct CUmod_st *CUmodule;
|
||||
typedef struct CUmipmappedArray_st *CUmipmappedArray;
|
||||
typedef struct CUgraphicsResource_st *CUgraphicsResource;
|
||||
typedef struct CUextMemory_st *CUexternalMemory;
|
||||
typedef struct CUextSemaphore_st *CUexternalSemaphore;
|
||||
|
||||
typedef struct CUlinkState_st *CUlinkState;
|
||||
|
||||
typedef enum cudaError_enum {
|
||||
CUDA_SUCCESS = 0,
|
||||
CUDA_ERROR_NOT_READY = 600
|
||||
} CUresult;
|
||||
|
||||
/**
|
||||
* Device properties (subset)
|
||||
*/
|
||||
typedef enum CUdevice_attribute_enum {
|
||||
CU_DEVICE_ATTRIBUTE_CLOCK_RATE = 13,
|
||||
CU_DEVICE_ATTRIBUTE_TEXTURE_ALIGNMENT = 14,
|
||||
CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT = 16,
|
||||
CU_DEVICE_ATTRIBUTE_INTEGRATED = 18,
|
||||
CU_DEVICE_ATTRIBUTE_CAN_MAP_HOST_MEMORY = 19,
|
||||
CU_DEVICE_ATTRIBUTE_COMPUTE_MODE = 20,
|
||||
CU_DEVICE_ATTRIBUTE_CONCURRENT_KERNELS = 31,
|
||||
CU_DEVICE_ATTRIBUTE_PCI_BUS_ID = 33,
|
||||
CU_DEVICE_ATTRIBUTE_PCI_DEVICE_ID = 34,
|
||||
CU_DEVICE_ATTRIBUTE_TCC_DRIVER = 35,
|
||||
CU_DEVICE_ATTRIBUTE_MEMORY_CLOCK_RATE = 36,
|
||||
CU_DEVICE_ATTRIBUTE_GLOBAL_MEMORY_BUS_WIDTH = 37,
|
||||
CU_DEVICE_ATTRIBUTE_ASYNC_ENGINE_COUNT = 40,
|
||||
CU_DEVICE_ATTRIBUTE_UNIFIED_ADDRESSING = 41,
|
||||
CU_DEVICE_ATTRIBUTE_PCI_DOMAIN_ID = 50,
|
||||
CU_DEVICE_ATTRIBUTE_TEXTURE_PITCH_ALIGNMENT = 51,
|
||||
CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR = 75,
|
||||
CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR = 76,
|
||||
CU_DEVICE_ATTRIBUTE_MANAGED_MEMORY = 83,
|
||||
CU_DEVICE_ATTRIBUTE_MULTI_GPU_BOARD = 84,
|
||||
CU_DEVICE_ATTRIBUTE_MULTI_GPU_BOARD_GROUP_ID = 85,
|
||||
} CUdevice_attribute;
|
||||
|
||||
typedef enum CUarray_format_enum {
|
||||
CU_AD_FORMAT_UNSIGNED_INT8 = 0x01,
|
||||
CU_AD_FORMAT_UNSIGNED_INT16 = 0x02,
|
||||
CU_AD_FORMAT_UNSIGNED_INT32 = 0x03,
|
||||
CU_AD_FORMAT_SIGNED_INT8 = 0x08,
|
||||
CU_AD_FORMAT_SIGNED_INT16 = 0x09,
|
||||
CU_AD_FORMAT_SIGNED_INT32 = 0x0a,
|
||||
CU_AD_FORMAT_HALF = 0x10,
|
||||
CU_AD_FORMAT_FLOAT = 0x20
|
||||
} CUarray_format;
|
||||
|
||||
typedef enum CUmemorytype_enum {
|
||||
CU_MEMORYTYPE_HOST = 1,
|
||||
CU_MEMORYTYPE_DEVICE = 2,
|
||||
CU_MEMORYTYPE_ARRAY = 3
|
||||
} CUmemorytype;
|
||||
|
||||
typedef enum CUlimit_enum {
|
||||
CU_LIMIT_STACK_SIZE = 0,
|
||||
CU_LIMIT_PRINTF_FIFO_SIZE = 1,
|
||||
CU_LIMIT_MALLOC_HEAP_SIZE = 2,
|
||||
CU_LIMIT_DEV_RUNTIME_SYNC_DEPTH = 3,
|
||||
CU_LIMIT_DEV_RUNTIME_PENDING_LAUNCH_COUNT = 4
|
||||
} CUlimit;
|
||||
|
||||
typedef enum CUresourcetype_enum {
|
||||
CU_RESOURCE_TYPE_ARRAY = 0x00,
|
||||
CU_RESOURCE_TYPE_MIPMAPPED_ARRAY = 0x01,
|
||||
CU_RESOURCE_TYPE_LINEAR = 0x02,
|
||||
CU_RESOURCE_TYPE_PITCH2D = 0x03
|
||||
} CUresourcetype;
|
||||
|
||||
typedef enum CUaddress_mode_enum {
|
||||
CU_TR_ADDRESS_MODE_WRAP = 0,
|
||||
CU_TR_ADDRESS_MODE_CLAMP = 1,
|
||||
CU_TR_ADDRESS_MODE_MIRROR = 2,
|
||||
CU_TR_ADDRESS_MODE_BORDER = 3
|
||||
} CUaddress_mode;
|
||||
|
||||
typedef enum CUfilter_mode_enum {
|
||||
CU_TR_FILTER_MODE_POINT = 0,
|
||||
CU_TR_FILTER_MODE_LINEAR = 1
|
||||
} CUfilter_mode;
|
||||
|
||||
typedef enum CUgraphicsRegisterFlags_enum {
|
||||
CU_GRAPHICS_REGISTER_FLAGS_NONE = 0,
|
||||
CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY = 1,
|
||||
CU_GRAPHICS_REGISTER_FLAGS_WRITE_DISCARD = 2,
|
||||
CU_GRAPHICS_REGISTER_FLAGS_SURFACE_LDST = 4,
|
||||
CU_GRAPHICS_REGISTER_FLAGS_TEXTURE_GATHER = 8
|
||||
} CUgraphicsRegisterFlags;
|
||||
|
||||
typedef enum CUexternalMemoryHandleType_enum {
|
||||
CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD = 1,
|
||||
CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32 = 2,
|
||||
CU_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT = 3,
|
||||
CU_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP = 4,
|
||||
CU_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE = 5,
|
||||
} CUexternalMemoryHandleType;
|
||||
|
||||
typedef enum CUexternalSemaphoreHandleType_enum {
|
||||
CU_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD = 1,
|
||||
CU_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32 = 2,
|
||||
CU_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT = 3,
|
||||
CU_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE = 4
|
||||
} CUexternalSemaphoreHandleType;
|
||||
|
||||
typedef enum CUjit_option_enum
|
||||
{
|
||||
CU_JIT_MAX_REGISTERS = 0,
|
||||
CU_JIT_THREADS_PER_BLOCK = 1,
|
||||
CU_JIT_WALL_TIME = 2,
|
||||
CU_JIT_INFO_LOG_BUFFER = 3,
|
||||
CU_JIT_INFO_LOG_BUFFER_SIZE_BYTES = 4,
|
||||
CU_JIT_ERROR_LOG_BUFFER = 5,
|
||||
CU_JIT_ERROR_LOG_BUFFER_SIZE_BYTES = 6,
|
||||
CU_JIT_OPTIMIZATION_LEVEL = 7,
|
||||
CU_JIT_TARGET_FROM_CUCONTEXT = 8,
|
||||
CU_JIT_TARGET = 9,
|
||||
CU_JIT_FALLBACK_STRATEGY = 10,
|
||||
CU_JIT_GENERATE_DEBUG_INFO = 11,
|
||||
CU_JIT_LOG_VERBOSE = 12,
|
||||
CU_JIT_GENERATE_LINE_INFO = 13,
|
||||
CU_JIT_CACHE_MODE = 14,
|
||||
CU_JIT_NEW_SM3X_OPT = 15,
|
||||
CU_JIT_FAST_COMPILE = 16,
|
||||
CU_JIT_GLOBAL_SYMBOL_NAMES = 17,
|
||||
CU_JIT_GLOBAL_SYMBOL_ADDRESSES = 18,
|
||||
CU_JIT_GLOBAL_SYMBOL_COUNT = 19,
|
||||
CU_JIT_NUM_OPTIONS
|
||||
} CUjit_option;
|
||||
|
||||
typedef enum CUjitInputType_enum
|
||||
{
|
||||
CU_JIT_INPUT_CUBIN = 0,
|
||||
CU_JIT_INPUT_PTX = 1,
|
||||
CU_JIT_INPUT_FATBINARY = 2,
|
||||
CU_JIT_INPUT_OBJECT = 3,
|
||||
CU_JIT_INPUT_LIBRARY = 4,
|
||||
CU_JIT_NUM_INPUT_TYPES
|
||||
} CUjitInputType;
|
||||
|
||||
#ifndef CU_UUID_HAS_BEEN_DEFINED
|
||||
#define CU_UUID_HAS_BEEN_DEFINED
|
||||
typedef struct CUuuid_st {
|
||||
char bytes[16];
|
||||
} CUuuid;
|
||||
#endif
|
||||
|
||||
typedef struct CUDA_MEMCPY2D_st {
|
||||
size_t srcXInBytes;
|
||||
size_t srcY;
|
||||
CUmemorytype srcMemoryType;
|
||||
const void *srcHost;
|
||||
CUdeviceptr srcDevice;
|
||||
CUarray srcArray;
|
||||
size_t srcPitch;
|
||||
|
||||
size_t dstXInBytes;
|
||||
size_t dstY;
|
||||
CUmemorytype dstMemoryType;
|
||||
void *dstHost;
|
||||
CUdeviceptr dstDevice;
|
||||
CUarray dstArray;
|
||||
size_t dstPitch;
|
||||
|
||||
size_t WidthInBytes;
|
||||
size_t Height;
|
||||
} CUDA_MEMCPY2D;
|
||||
|
||||
typedef struct CUDA_RESOURCE_DESC_st {
|
||||
CUresourcetype resType;
|
||||
union {
|
||||
struct {
|
||||
CUarray hArray;
|
||||
} array;
|
||||
struct {
|
||||
CUmipmappedArray hMipmappedArray;
|
||||
} mipmap;
|
||||
struct {
|
||||
CUdeviceptr devPtr;
|
||||
CUarray_format format;
|
||||
unsigned int numChannels;
|
||||
size_t sizeInBytes;
|
||||
} linear;
|
||||
struct {
|
||||
CUdeviceptr devPtr;
|
||||
CUarray_format format;
|
||||
unsigned int numChannels;
|
||||
size_t width;
|
||||
size_t height;
|
||||
size_t pitchInBytes;
|
||||
} pitch2D;
|
||||
struct {
|
||||
int reserved[32];
|
||||
} reserved;
|
||||
} res;
|
||||
unsigned int flags;
|
||||
} CUDA_RESOURCE_DESC;
|
||||
|
||||
typedef struct CUDA_TEXTURE_DESC_st {
|
||||
CUaddress_mode addressMode[3];
|
||||
CUfilter_mode filterMode;
|
||||
unsigned int flags;
|
||||
unsigned int maxAnisotropy;
|
||||
CUfilter_mode mipmapFilterMode;
|
||||
float mipmapLevelBias;
|
||||
float minMipmapLevelClamp;
|
||||
float maxMipmapLevelClamp;
|
||||
float borderColor[4];
|
||||
int reserved[12];
|
||||
} CUDA_TEXTURE_DESC;
|
||||
|
||||
/* Unused type */
|
||||
typedef struct CUDA_RESOURCE_VIEW_DESC_st CUDA_RESOURCE_VIEW_DESC;
|
||||
|
||||
typedef unsigned int GLenum;
|
||||
typedef unsigned int GLuint;
|
||||
|
||||
typedef enum CUGLDeviceList_enum {
|
||||
CU_GL_DEVICE_LIST_ALL = 1,
|
||||
CU_GL_DEVICE_LIST_CURRENT_FRAME = 2,
|
||||
CU_GL_DEVICE_LIST_NEXT_FRAME = 3,
|
||||
} CUGLDeviceList;
|
||||
|
||||
typedef struct CUDA_EXTERNAL_MEMORY_HANDLE_DESC_st {
|
||||
CUexternalMemoryHandleType type;
|
||||
union {
|
||||
int fd;
|
||||
struct {
|
||||
void *handle;
|
||||
const void *name;
|
||||
} win32;
|
||||
} handle;
|
||||
unsigned long long size;
|
||||
unsigned int flags;
|
||||
unsigned int reserved[16];
|
||||
} CUDA_EXTERNAL_MEMORY_HANDLE_DESC;
|
||||
|
||||
typedef struct CUDA_EXTERNAL_MEMORY_BUFFER_DESC_st {
|
||||
unsigned long long offset;
|
||||
unsigned long long size;
|
||||
unsigned int flags;
|
||||
unsigned int reserved[16];
|
||||
} CUDA_EXTERNAL_MEMORY_BUFFER_DESC;
|
||||
|
||||
typedef struct CUDA_EXTERNAL_SEMAPHORE_HANDLE_DESC_st {
|
||||
CUexternalSemaphoreHandleType type;
|
||||
union {
|
||||
int fd;
|
||||
struct {
|
||||
void *handle;
|
||||
const void *name;
|
||||
} win32;
|
||||
} handle;
|
||||
unsigned int flags;
|
||||
unsigned int reserved[16];
|
||||
} CUDA_EXTERNAL_SEMAPHORE_HANDLE_DESC;
|
||||
|
||||
typedef struct CUDA_EXTERNAL_SEMAPHORE_SIGNAL_PARAMS_st {
|
||||
struct {
|
||||
struct {
|
||||
unsigned long long value;
|
||||
} fence;
|
||||
unsigned int reserved[16];
|
||||
} params;
|
||||
unsigned int flags;
|
||||
unsigned int reserved[16];
|
||||
} CUDA_EXTERNAL_SEMAPHORE_SIGNAL_PARAMS;
|
||||
|
||||
typedef CUDA_EXTERNAL_SEMAPHORE_SIGNAL_PARAMS CUDA_EXTERNAL_SEMAPHORE_WAIT_PARAMS;
|
||||
|
||||
typedef struct CUDA_ARRAY3D_DESCRIPTOR_st {
|
||||
size_t Width;
|
||||
size_t Height;
|
||||
size_t Depth;
|
||||
|
||||
CUarray_format Format;
|
||||
unsigned int NumChannels;
|
||||
unsigned int Flags;
|
||||
} CUDA_ARRAY3D_DESCRIPTOR;
|
||||
|
||||
typedef struct CUDA_EXTERNAL_MEMORY_MIPMAPPED_ARRAY_DESC_st {
|
||||
unsigned long long offset;
|
||||
CUDA_ARRAY3D_DESCRIPTOR arrayDesc;
|
||||
unsigned int numLevels;
|
||||
unsigned int reserved[16];
|
||||
} CUDA_EXTERNAL_MEMORY_MIPMAPPED_ARRAY_DESC;
|
||||
|
||||
#define CU_STREAM_NON_BLOCKING 1
|
||||
#define CU_EVENT_BLOCKING_SYNC 1
|
||||
#define CU_EVENT_DISABLE_TIMING 2
|
||||
#define CU_TRSF_READ_AS_INTEGER 1
|
||||
|
||||
typedef void CUDAAPI CUstreamCallback(CUstream hStream, CUresult status, void *userdata);
|
||||
|
||||
typedef CUresult CUDAAPI tcuInit(unsigned int Flags);
|
||||
typedef CUresult CUDAAPI tcuDeviceGetCount(int *count);
|
||||
typedef CUresult CUDAAPI tcuDeviceGet(CUdevice *device, int ordinal);
|
||||
typedef CUresult CUDAAPI tcuDeviceGetAttribute(int *pi, CUdevice_attribute attrib, CUdevice dev);
|
||||
typedef CUresult CUDAAPI tcuDeviceGetName(char *name, int len, CUdevice dev);
|
||||
typedef CUresult CUDAAPI tcuDeviceGetUuid(CUuuid *uuid, CUdevice dev);
|
||||
typedef CUresult CUDAAPI tcuDeviceComputeCapability(int *major, int *minor, CUdevice dev);
|
||||
typedef CUresult CUDAAPI tcuCtxCreate_v2(CUcontext *pctx, unsigned int flags, CUdevice dev);
|
||||
typedef CUresult CUDAAPI tcuCtxSetLimit(CUlimit limit, size_t value);
|
||||
typedef CUresult CUDAAPI tcuCtxPushCurrent_v2(CUcontext pctx);
|
||||
typedef CUresult CUDAAPI tcuCtxPopCurrent_v2(CUcontext *pctx);
|
||||
typedef CUresult CUDAAPI tcuCtxDestroy_v2(CUcontext ctx);
|
||||
typedef CUresult CUDAAPI tcuMemAlloc_v2(CUdeviceptr *dptr, size_t bytesize);
|
||||
typedef CUresult CUDAAPI tcuMemAllocPitch_v2(CUdeviceptr *dptr, size_t *pPitch, size_t WidthInBytes, size_t Height, unsigned int ElementSizeBytes);
|
||||
typedef CUresult CUDAAPI tcuMemsetD8Async(CUdeviceptr dstDevice, unsigned char uc, size_t N, CUstream hStream);
|
||||
typedef CUresult CUDAAPI tcuMemFree_v2(CUdeviceptr dptr);
|
||||
typedef CUresult CUDAAPI tcuMemcpy(CUdeviceptr dst, CUdeviceptr src, size_t bytesize);
|
||||
typedef CUresult CUDAAPI tcuMemcpyAsync(CUdeviceptr dst, CUdeviceptr src, size_t bytesize, CUstream hStream);
|
||||
typedef CUresult CUDAAPI tcuMemcpy2D_v2(const CUDA_MEMCPY2D *pcopy);
|
||||
typedef CUresult CUDAAPI tcuMemcpy2DAsync_v2(const CUDA_MEMCPY2D *pcopy, CUstream hStream);
|
||||
typedef CUresult CUDAAPI tcuGetErrorName(CUresult error, const char** pstr);
|
||||
typedef CUresult CUDAAPI tcuGetErrorString(CUresult error, const char** pstr);
|
||||
typedef CUresult CUDAAPI tcuCtxGetDevice(CUdevice *device);
|
||||
|
||||
typedef CUresult CUDAAPI tcuDevicePrimaryCtxRetain(CUcontext *pctx, CUdevice dev);
|
||||
typedef CUresult CUDAAPI tcuDevicePrimaryCtxRelease(CUdevice dev);
|
||||
typedef CUresult CUDAAPI tcuDevicePrimaryCtxSetFlags(CUdevice dev, unsigned int flags);
|
||||
typedef CUresult CUDAAPI tcuDevicePrimaryCtxGetState(CUdevice dev, unsigned int *flags, int *active);
|
||||
typedef CUresult CUDAAPI tcuDevicePrimaryCtxReset(CUdevice dev);
|
||||
|
||||
typedef CUresult CUDAAPI tcuStreamCreate(CUstream *phStream, unsigned int flags);
|
||||
typedef CUresult CUDAAPI tcuStreamQuery(CUstream hStream);
|
||||
typedef CUresult CUDAAPI tcuStreamSynchronize(CUstream hStream);
|
||||
typedef CUresult CUDAAPI tcuStreamDestroy_v2(CUstream hStream);
|
||||
typedef CUresult CUDAAPI tcuStreamAddCallback(CUstream hStream, CUstreamCallback *callback, void *userdata, unsigned int flags);
|
||||
typedef CUresult CUDAAPI tcuEventCreate(CUevent *phEvent, unsigned int flags);
|
||||
typedef CUresult CUDAAPI tcuEventDestroy_v2(CUevent hEvent);
|
||||
typedef CUresult CUDAAPI tcuEventSynchronize(CUevent hEvent);
|
||||
typedef CUresult CUDAAPI tcuEventQuery(CUevent hEvent);
|
||||
typedef CUresult CUDAAPI tcuEventRecord(CUevent hEvent, CUstream hStream);
|
||||
|
||||
typedef CUresult CUDAAPI tcuLaunchKernel(CUfunction f, unsigned int gridDimX, unsigned int gridDimY, unsigned int gridDimZ, unsigned int blockDimX, unsigned int blockDimY, unsigned int blockDimZ, unsigned int sharedMemBytes, CUstream hStream, void** kernelParams, void** extra);
|
||||
typedef CUresult CUDAAPI tcuLinkCreate(unsigned int numOptions, CUjit_option* options, void** optionValues, CUlinkState* stateOut);
|
||||
typedef CUresult CUDAAPI tcuLinkAddData(CUlinkState state, CUjitInputType type, void* data, size_t size, const char* name, unsigned int numOptions, CUjit_option* options, void** optionValues);
|
||||
typedef CUresult CUDAAPI tcuLinkComplete(CUlinkState state, void** cubinOut, size_t* sizeOut);
|
||||
typedef CUresult CUDAAPI tcuLinkDestroy(CUlinkState state);
|
||||
typedef CUresult CUDAAPI tcuModuleLoadData(CUmodule* module, const void* image);
|
||||
typedef CUresult CUDAAPI tcuModuleUnload(CUmodule hmod);
|
||||
typedef CUresult CUDAAPI tcuModuleGetFunction(CUfunction* hfunc, CUmodule hmod, const char* name);
|
||||
typedef CUresult CUDAAPI tcuModuleGetGlobal(CUdeviceptr *dptr, size_t *bytes, CUmodule hmod, const char* name);
|
||||
typedef CUresult CUDAAPI tcuTexObjectCreate(CUtexObject* pTexObject, const CUDA_RESOURCE_DESC* pResDesc, const CUDA_TEXTURE_DESC* pTexDesc, const CUDA_RESOURCE_VIEW_DESC* pResViewDesc);
|
||||
typedef CUresult CUDAAPI tcuTexObjectDestroy(CUtexObject texObject);
|
||||
|
||||
typedef CUresult CUDAAPI tcuGLGetDevices_v2(unsigned int* pCudaDeviceCount, CUdevice* pCudaDevices, unsigned int cudaDeviceCount, CUGLDeviceList deviceList);
|
||||
typedef CUresult CUDAAPI tcuGraphicsGLRegisterImage(CUgraphicsResource* pCudaResource, GLuint image, GLenum target, unsigned int Flags);
|
||||
typedef CUresult CUDAAPI tcuGraphicsUnregisterResource(CUgraphicsResource resource);
|
||||
typedef CUresult CUDAAPI tcuGraphicsMapResources(unsigned int count, CUgraphicsResource* resources, CUstream hStream);
|
||||
typedef CUresult CUDAAPI tcuGraphicsUnmapResources(unsigned int count, CUgraphicsResource* resources, CUstream hStream);
|
||||
typedef CUresult CUDAAPI tcuGraphicsSubResourceGetMappedArray(CUarray* pArray, CUgraphicsResource resource, unsigned int arrayIndex, unsigned int mipLevel);
|
||||
|
||||
typedef CUresult CUDAAPI tcuImportExternalMemory(CUexternalMemory* extMem_out, const CUDA_EXTERNAL_MEMORY_HANDLE_DESC* memHandleDesc);
|
||||
typedef CUresult CUDAAPI tcuDestroyExternalMemory(CUexternalMemory extMem);
|
||||
typedef CUresult CUDAAPI tcuExternalMemoryGetMappedBuffer(CUdeviceptr* devPtr, CUexternalMemory extMem, const CUDA_EXTERNAL_MEMORY_BUFFER_DESC* bufferDesc);
|
||||
typedef CUresult CUDAAPI tcuExternalMemoryGetMappedMipmappedArray(CUmipmappedArray* mipmap, CUexternalMemory extMem, const CUDA_EXTERNAL_MEMORY_MIPMAPPED_ARRAY_DESC* mipmapDesc);
|
||||
typedef CUresult CUDAAPI tcuMipmappedArrayGetLevel(CUarray* pLevelArray, CUmipmappedArray hMipmappedArray, unsigned int level);
|
||||
typedef CUresult CUDAAPI tcuMipmappedArrayDestroy(CUmipmappedArray hMipmappedArray);
|
||||
|
||||
typedef CUresult CUDAAPI tcuImportExternalSemaphore(CUexternalSemaphore* extSem_out, const CUDA_EXTERNAL_SEMAPHORE_HANDLE_DESC* semHandleDesc);
|
||||
typedef CUresult CUDAAPI tcuDestroyExternalSemaphore(CUexternalSemaphore extSem);
|
||||
typedef CUresult CUDAAPI tcuSignalExternalSemaphoresAsync(const CUexternalSemaphore* extSemArray, const CUDA_EXTERNAL_SEMAPHORE_SIGNAL_PARAMS* paramsArray, unsigned int numExtSems, CUstream stream);
|
||||
typedef CUresult CUDAAPI tcuWaitExternalSemaphoresAsync(const CUexternalSemaphore* extSemArray, const CUDA_EXTERNAL_SEMAPHORE_WAIT_PARAMS* paramsArray, unsigned int numExtSems, CUstream stream);
|
||||
#endif
|
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* This copyright notice applies to this header file only:
|
||||
*
|
||||
* Copyright (c) 2016
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the software, and to permit persons to whom the
|
||||
* software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef FFNV_CUDA_DYNLINK_LOADER_H
|
||||
#define FFNV_CUDA_DYNLINK_LOADER_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dynlink_cuda.h"
|
||||
#include "nvEncodeAPI.h"
|
||||
|
||||
#if defined(_WIN32) && (!defined(FFNV_LOAD_FUNC) || !defined(FFNV_SYM_FUNC) || !defined(FFNV_LIB_HANDLE))
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifndef FFNV_LIB_HANDLE
|
||||
# if defined(_WIN32)
|
||||
# define FFNV_LIB_HANDLE HMODULE
|
||||
# else
|
||||
# define FFNV_LIB_HANDLE void*
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
# define CUDA_LIBNAME "nvcuda.dll"
|
||||
# define NVCUVID_LIBNAME "nvcuvid.dll"
|
||||
# if defined(_WIN64) || defined(__CYGWIN64__)
|
||||
# define NVENC_LIBNAME "nvEncodeAPI64.dll"
|
||||
# else
|
||||
# define NVENC_LIBNAME "nvEncodeAPI.dll"
|
||||
# endif
|
||||
#else
|
||||
# define CUDA_LIBNAME "libcuda.so.1"
|
||||
# define NVCUVID_LIBNAME "libnvcuvid.so.1"
|
||||
# define NVENC_LIBNAME "libnvidia-encode.so.1"
|
||||
#endif
|
||||
|
||||
#if !defined(FFNV_LOAD_FUNC) || !defined(FFNV_SYM_FUNC)
|
||||
# ifdef _WIN32
|
||||
# define FFNV_LOAD_FUNC(path) LoadLibrary(TEXT(path))
|
||||
# define FFNV_SYM_FUNC(lib, sym) GetProcAddress((lib), (sym))
|
||||
# define FFNV_FREE_FUNC(lib) FreeLibrary(lib)
|
||||
# else
|
||||
# include <dlfcn.h>
|
||||
# define FFNV_LOAD_FUNC(path) dlopen((path), RTLD_LAZY)
|
||||
# define FFNV_SYM_FUNC(lib, sym) dlsym((lib), (sym))
|
||||
# define FFNV_FREE_FUNC(lib) dlclose(lib)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if !defined(FFNV_LOG_FUNC) || !defined(FFNV_DEBUG_LOG_FUNC)
|
||||
# include <stdio.h>
|
||||
# define FFNV_LOG_FUNC(logctx, msg, ...) fprintf(stderr, (msg), __VA_ARGS__)
|
||||
# define FFNV_DEBUG_LOG_FUNC(logctx, msg, ...)
|
||||
#endif
|
||||
|
||||
#define LOAD_LIBRARY(l, path) \
|
||||
do { \
|
||||
if (!((l) = FFNV_LOAD_FUNC(path))) { \
|
||||
FFNV_LOG_FUNC(logctx, "Cannot load %s\n", path); \
|
||||
ret = -1; \
|
||||
goto error; \
|
||||
} \
|
||||
FFNV_DEBUG_LOG_FUNC(logctx, "Loaded lib: %s\n", path); \
|
||||
} while (0)
|
||||
|
||||
#define LOAD_SYMBOL(fun, tp, symbol) \
|
||||
do { \
|
||||
if (!((f->fun) = (tp*)FFNV_SYM_FUNC(f->lib, symbol))) { \
|
||||
FFNV_LOG_FUNC(logctx, "Cannot load %s\n", symbol); \
|
||||
ret = -1; \
|
||||
goto error; \
|
||||
} \
|
||||
FFNV_DEBUG_LOG_FUNC(logctx, "Loaded sym: %s\n", symbol); \
|
||||
} while (0)
|
||||
|
||||
#define LOAD_SYMBOL_OPT(fun, tp, symbol) \
|
||||
do { \
|
||||
if (!((f->fun) = (tp*)FFNV_SYM_FUNC(f->lib, symbol))) { \
|
||||
FFNV_DEBUG_LOG_FUNC(logctx, "Cannot load optional %s\n", symbol); \
|
||||
} else { \
|
||||
FFNV_DEBUG_LOG_FUNC(logctx, "Loaded sym: %s\n", symbol); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define GENERIC_LOAD_FUNC_PREAMBLE(T, n, N) \
|
||||
T *f; \
|
||||
int ret; \
|
||||
\
|
||||
n##_free_functions(functions); \
|
||||
\
|
||||
f = *functions = (T*)calloc(1, sizeof(*f)); \
|
||||
if (!f) \
|
||||
return -1; \
|
||||
\
|
||||
LOAD_LIBRARY(f->lib, N);
|
||||
|
||||
#define GENERIC_LOAD_FUNC_FINALE(n) \
|
||||
return 0; \
|
||||
error: \
|
||||
n##_free_functions(functions); \
|
||||
return ret;
|
||||
|
||||
#define GENERIC_FREE_FUNC() \
|
||||
if (!functions) \
|
||||
return; \
|
||||
if (*functions && (*functions)->lib) \
|
||||
FFNV_FREE_FUNC((*functions)->lib); \
|
||||
free(*functions); \
|
||||
*functions = NULL;
|
||||
|
||||
#ifdef FFNV_DYNLINK_CUDA_H
|
||||
typedef struct CudaFunctions {
|
||||
tcuInit *cuInit;
|
||||
tcuDeviceGetCount *cuDeviceGetCount;
|
||||
tcuDeviceGet *cuDeviceGet;
|
||||
tcuDeviceGetAttribute *cuDeviceGetAttribute;
|
||||
tcuDeviceGetName *cuDeviceGetName;
|
||||
tcuDeviceGetUuid *cuDeviceGetUuid;
|
||||
tcuDeviceComputeCapability *cuDeviceComputeCapability;
|
||||
tcuCtxCreate_v2 *cuCtxCreate;
|
||||
tcuCtxSetLimit *cuCtxSetLimit;
|
||||
tcuCtxPushCurrent_v2 *cuCtxPushCurrent;
|
||||
tcuCtxPopCurrent_v2 *cuCtxPopCurrent;
|
||||
tcuCtxDestroy_v2 *cuCtxDestroy;
|
||||
tcuMemAlloc_v2 *cuMemAlloc;
|
||||
tcuMemAllocPitch_v2 *cuMemAllocPitch;
|
||||
tcuMemsetD8Async *cuMemsetD8Async;
|
||||
tcuMemFree_v2 *cuMemFree;
|
||||
tcuMemcpy *cuMemcpy;
|
||||
tcuMemcpyAsync *cuMemcpyAsync;
|
||||
tcuMemcpy2D_v2 *cuMemcpy2D;
|
||||
tcuMemcpy2DAsync_v2 *cuMemcpy2DAsync;
|
||||
tcuGetErrorName *cuGetErrorName;
|
||||
tcuGetErrorString *cuGetErrorString;
|
||||
tcuCtxGetDevice *cuCtxGetDevice;
|
||||
|
||||
tcuDevicePrimaryCtxRetain *cuDevicePrimaryCtxRetain;
|
||||
tcuDevicePrimaryCtxRelease *cuDevicePrimaryCtxRelease;
|
||||
tcuDevicePrimaryCtxSetFlags *cuDevicePrimaryCtxSetFlags;
|
||||
tcuDevicePrimaryCtxGetState *cuDevicePrimaryCtxGetState;
|
||||
tcuDevicePrimaryCtxReset *cuDevicePrimaryCtxReset;
|
||||
|
||||
tcuStreamCreate *cuStreamCreate;
|
||||
tcuStreamQuery *cuStreamQuery;
|
||||
tcuStreamSynchronize *cuStreamSynchronize;
|
||||
tcuStreamDestroy_v2 *cuStreamDestroy;
|
||||
tcuStreamAddCallback *cuStreamAddCallback;
|
||||
tcuEventCreate *cuEventCreate;
|
||||
tcuEventDestroy_v2 *cuEventDestroy;
|
||||
tcuEventSynchronize *cuEventSynchronize;
|
||||
tcuEventQuery *cuEventQuery;
|
||||
tcuEventRecord *cuEventRecord;
|
||||
|
||||
tcuLaunchKernel *cuLaunchKernel;
|
||||
tcuLinkCreate *cuLinkCreate;
|
||||
tcuLinkAddData *cuLinkAddData;
|
||||
tcuLinkComplete *cuLinkComplete;
|
||||
tcuLinkDestroy *cuLinkDestroy;
|
||||
tcuModuleLoadData *cuModuleLoadData;
|
||||
tcuModuleUnload *cuModuleUnload;
|
||||
tcuModuleGetFunction *cuModuleGetFunction;
|
||||
tcuModuleGetGlobal *cuModuleGetGlobal;
|
||||
tcuTexObjectCreate *cuTexObjectCreate;
|
||||
tcuTexObjectDestroy *cuTexObjectDestroy;
|
||||
|
||||
tcuGLGetDevices_v2 *cuGLGetDevices;
|
||||
tcuGraphicsGLRegisterImage *cuGraphicsGLRegisterImage;
|
||||
tcuGraphicsUnregisterResource *cuGraphicsUnregisterResource;
|
||||
tcuGraphicsMapResources *cuGraphicsMapResources;
|
||||
tcuGraphicsUnmapResources *cuGraphicsUnmapResources;
|
||||
tcuGraphicsSubResourceGetMappedArray *cuGraphicsSubResourceGetMappedArray;
|
||||
|
||||
tcuImportExternalMemory *cuImportExternalMemory;
|
||||
tcuDestroyExternalMemory *cuDestroyExternalMemory;
|
||||
tcuExternalMemoryGetMappedBuffer *cuExternalMemoryGetMappedBuffer;
|
||||
tcuExternalMemoryGetMappedMipmappedArray *cuExternalMemoryGetMappedMipmappedArray;
|
||||
tcuMipmappedArrayDestroy *cuMipmappedArrayDestroy;
|
||||
|
||||
tcuMipmappedArrayGetLevel *cuMipmappedArrayGetLevel;
|
||||
|
||||
tcuImportExternalSemaphore *cuImportExternalSemaphore;
|
||||
tcuDestroyExternalSemaphore *cuDestroyExternalSemaphore;
|
||||
tcuSignalExternalSemaphoresAsync *cuSignalExternalSemaphoresAsync;
|
||||
tcuWaitExternalSemaphoresAsync *cuWaitExternalSemaphoresAsync;
|
||||
|
||||
FFNV_LIB_HANDLE lib;
|
||||
} CudaFunctions;
|
||||
#else
|
||||
typedef struct CudaFunctions CudaFunctions;
|
||||
#endif
|
||||
|
||||
typedef NVENCSTATUS NVENCAPI tNvEncodeAPICreateInstance(NV_ENCODE_API_FUNCTION_LIST *functionList);
|
||||
typedef NVENCSTATUS NVENCAPI tNvEncodeAPIGetMaxSupportedVersion(uint32_t* version);
|
||||
|
||||
typedef struct NvencFunctions {
|
||||
tNvEncodeAPICreateInstance *NvEncodeAPICreateInstance;
|
||||
tNvEncodeAPIGetMaxSupportedVersion *NvEncodeAPIGetMaxSupportedVersion;
|
||||
|
||||
FFNV_LIB_HANDLE lib;
|
||||
} NvencFunctions;
|
||||
|
||||
#ifdef FFNV_DYNLINK_CUDA_H
|
||||
static inline void cuda_free_functions(CudaFunctions **functions)
|
||||
{
|
||||
GENERIC_FREE_FUNC();
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void nvenc_free_functions(NvencFunctions **functions)
|
||||
{
|
||||
GENERIC_FREE_FUNC();
|
||||
}
|
||||
|
||||
#ifdef FFNV_DYNLINK_CUDA_H
|
||||
static inline int cuda_load_functions(CudaFunctions **functions)
|
||||
{
|
||||
GENERIC_LOAD_FUNC_PREAMBLE(CudaFunctions, cuda, CUDA_LIBNAME);
|
||||
|
||||
LOAD_SYMBOL(cuInit, tcuInit, "cuInit");
|
||||
LOAD_SYMBOL(cuDeviceGetCount, tcuDeviceGetCount, "cuDeviceGetCount");
|
||||
LOAD_SYMBOL(cuDeviceGet, tcuDeviceGet, "cuDeviceGet");
|
||||
LOAD_SYMBOL(cuDeviceGetAttribute, tcuDeviceGetAttribute, "cuDeviceGetAttribute");
|
||||
LOAD_SYMBOL(cuDeviceGetName, tcuDeviceGetName, "cuDeviceGetName");
|
||||
LOAD_SYMBOL(cuDeviceComputeCapability, tcuDeviceComputeCapability, "cuDeviceComputeCapability");
|
||||
LOAD_SYMBOL(cuCtxCreate, tcuCtxCreate_v2, "cuCtxCreate_v2");
|
||||
LOAD_SYMBOL(cuCtxSetLimit, tcuCtxSetLimit, "cuCtxSetLimit");
|
||||
LOAD_SYMBOL(cuCtxPushCurrent, tcuCtxPushCurrent_v2, "cuCtxPushCurrent_v2");
|
||||
LOAD_SYMBOL(cuCtxPopCurrent, tcuCtxPopCurrent_v2, "cuCtxPopCurrent_v2");
|
||||
LOAD_SYMBOL(cuCtxDestroy, tcuCtxDestroy_v2, "cuCtxDestroy_v2");
|
||||
LOAD_SYMBOL(cuMemAlloc, tcuMemAlloc_v2, "cuMemAlloc_v2");
|
||||
LOAD_SYMBOL(cuMemAllocPitch, tcuMemAllocPitch_v2, "cuMemAllocPitch_v2");
|
||||
LOAD_SYMBOL(cuMemsetD8Async, tcuMemsetD8Async, "cuMemsetD8Async");
|
||||
LOAD_SYMBOL(cuMemFree, tcuMemFree_v2, "cuMemFree_v2");
|
||||
LOAD_SYMBOL(cuMemcpy, tcuMemcpy, "cuMemcpy");
|
||||
LOAD_SYMBOL(cuMemcpyAsync, tcuMemcpyAsync, "cuMemcpyAsync");
|
||||
LOAD_SYMBOL(cuMemcpy2D, tcuMemcpy2D_v2, "cuMemcpy2D_v2");
|
||||
LOAD_SYMBOL(cuMemcpy2DAsync, tcuMemcpy2DAsync_v2, "cuMemcpy2DAsync_v2");
|
||||
LOAD_SYMBOL(cuGetErrorName, tcuGetErrorName, "cuGetErrorName");
|
||||
LOAD_SYMBOL(cuGetErrorString, tcuGetErrorString, "cuGetErrorString");
|
||||
LOAD_SYMBOL(cuCtxGetDevice, tcuCtxGetDevice, "cuCtxGetDevice");
|
||||
|
||||
LOAD_SYMBOL(cuDevicePrimaryCtxRetain, tcuDevicePrimaryCtxRetain, "cuDevicePrimaryCtxRetain");
|
||||
LOAD_SYMBOL(cuDevicePrimaryCtxRelease, tcuDevicePrimaryCtxRelease, "cuDevicePrimaryCtxRelease");
|
||||
LOAD_SYMBOL(cuDevicePrimaryCtxSetFlags, tcuDevicePrimaryCtxSetFlags, "cuDevicePrimaryCtxSetFlags");
|
||||
LOAD_SYMBOL(cuDevicePrimaryCtxGetState, tcuDevicePrimaryCtxGetState, "cuDevicePrimaryCtxGetState");
|
||||
LOAD_SYMBOL(cuDevicePrimaryCtxReset, tcuDevicePrimaryCtxReset, "cuDevicePrimaryCtxReset");
|
||||
|
||||
LOAD_SYMBOL(cuStreamCreate, tcuStreamCreate, "cuStreamCreate");
|
||||
LOAD_SYMBOL(cuStreamQuery, tcuStreamQuery, "cuStreamQuery");
|
||||
LOAD_SYMBOL(cuStreamSynchronize, tcuStreamSynchronize, "cuStreamSynchronize");
|
||||
LOAD_SYMBOL(cuStreamDestroy, tcuStreamDestroy_v2, "cuStreamDestroy_v2");
|
||||
LOAD_SYMBOL(cuStreamAddCallback, tcuStreamAddCallback, "cuStreamAddCallback");
|
||||
LOAD_SYMBOL(cuEventCreate, tcuEventCreate, "cuEventCreate");
|
||||
LOAD_SYMBOL(cuEventDestroy, tcuEventDestroy_v2, "cuEventDestroy_v2");
|
||||
LOAD_SYMBOL(cuEventSynchronize, tcuEventSynchronize, "cuEventSynchronize");
|
||||
LOAD_SYMBOL(cuEventQuery, tcuEventQuery, "cuEventQuery");
|
||||
LOAD_SYMBOL(cuEventRecord, tcuEventRecord, "cuEventRecord");
|
||||
|
||||
LOAD_SYMBOL(cuLaunchKernel, tcuLaunchKernel, "cuLaunchKernel");
|
||||
LOAD_SYMBOL(cuLinkCreate, tcuLinkCreate, "cuLinkCreate");
|
||||
LOAD_SYMBOL(cuLinkAddData, tcuLinkAddData, "cuLinkAddData");
|
||||
LOAD_SYMBOL(cuLinkComplete, tcuLinkComplete, "cuLinkComplete");
|
||||
LOAD_SYMBOL(cuLinkDestroy, tcuLinkDestroy, "cuLinkDestroy");
|
||||
LOAD_SYMBOL(cuModuleLoadData, tcuModuleLoadData, "cuModuleLoadData");
|
||||
LOAD_SYMBOL(cuModuleUnload, tcuModuleUnload, "cuModuleUnload");
|
||||
LOAD_SYMBOL(cuModuleGetFunction, tcuModuleGetFunction, "cuModuleGetFunction");
|
||||
LOAD_SYMBOL(cuModuleGetGlobal, tcuModuleGetGlobal, "cuModuleGetGlobal");
|
||||
LOAD_SYMBOL(cuTexObjectCreate, tcuTexObjectCreate, "cuTexObjectCreate");
|
||||
LOAD_SYMBOL(cuTexObjectDestroy, tcuTexObjectDestroy, "cuTexObjectDestroy");
|
||||
|
||||
LOAD_SYMBOL(cuGLGetDevices, tcuGLGetDevices_v2, "cuGLGetDevices_v2");
|
||||
LOAD_SYMBOL(cuGraphicsGLRegisterImage, tcuGraphicsGLRegisterImage, "cuGraphicsGLRegisterImage");
|
||||
LOAD_SYMBOL(cuGraphicsUnregisterResource, tcuGraphicsUnregisterResource, "cuGraphicsUnregisterResource");
|
||||
LOAD_SYMBOL(cuGraphicsMapResources, tcuGraphicsMapResources, "cuGraphicsMapResources");
|
||||
LOAD_SYMBOL(cuGraphicsUnmapResources, tcuGraphicsUnmapResources, "cuGraphicsUnmapResources");
|
||||
LOAD_SYMBOL(cuGraphicsSubResourceGetMappedArray, tcuGraphicsSubResourceGetMappedArray, "cuGraphicsSubResourceGetMappedArray");
|
||||
|
||||
LOAD_SYMBOL_OPT(cuDeviceGetUuid, tcuDeviceGetUuid, "cuDeviceGetUuid");
|
||||
LOAD_SYMBOL_OPT(cuImportExternalMemory, tcuImportExternalMemory, "cuImportExternalMemory");
|
||||
LOAD_SYMBOL_OPT(cuDestroyExternalMemory, tcuDestroyExternalMemory, "cuDestroyExternalMemory");
|
||||
LOAD_SYMBOL_OPT(cuExternalMemoryGetMappedBuffer, tcuExternalMemoryGetMappedBuffer, "cuExternalMemoryGetMappedBuffer");
|
||||
LOAD_SYMBOL_OPT(cuExternalMemoryGetMappedMipmappedArray, tcuExternalMemoryGetMappedMipmappedArray, "cuExternalMemoryGetMappedMipmappedArray");
|
||||
LOAD_SYMBOL_OPT(cuMipmappedArrayGetLevel, tcuMipmappedArrayGetLevel, "cuMipmappedArrayGetLevel");
|
||||
LOAD_SYMBOL_OPT(cuMipmappedArrayDestroy, tcuMipmappedArrayDestroy, "cuMipmappedArrayDestroy");
|
||||
|
||||
LOAD_SYMBOL_OPT(cuImportExternalSemaphore, tcuImportExternalSemaphore, "cuImportExternalSemaphore");
|
||||
LOAD_SYMBOL_OPT(cuDestroyExternalSemaphore, tcuDestroyExternalSemaphore, "cuDestroyExternalSemaphore");
|
||||
LOAD_SYMBOL_OPT(cuSignalExternalSemaphoresAsync, tcuSignalExternalSemaphoresAsync, "cuSignalExternalSemaphoresAsync");
|
||||
LOAD_SYMBOL_OPT(cuWaitExternalSemaphoresAsync, tcuWaitExternalSemaphoresAsync, "cuWaitExternalSemaphoresAsync");
|
||||
|
||||
GENERIC_LOAD_FUNC_FINALE(cuda);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int nvenc_load_functions(NvencFunctions **functions)
|
||||
{
|
||||
GENERIC_LOAD_FUNC_PREAMBLE(NvencFunctions, nvenc, NVENC_LIBNAME);
|
||||
|
||||
LOAD_SYMBOL(NvEncodeAPICreateInstance, tNvEncodeAPICreateInstance, "NvEncodeAPICreateInstance");
|
||||
LOAD_SYMBOL(NvEncodeAPIGetMaxSupportedVersion, tNvEncodeAPIGetMaxSupportedVersion, "NvEncodeAPIGetMaxSupportedVersion");
|
||||
|
||||
GENERIC_LOAD_FUNC_FINALE(nvenc);
|
||||
}
|
||||
|
||||
#undef GENERIC_LOAD_FUNC_PREAMBLE
|
||||
#undef LOAD_LIBRARY
|
||||
#undef LOAD_SYMBOL
|
||||
#undef GENERIC_LOAD_FUNC_FINALE
|
||||
#undef GENERIC_FREE_FUNC
|
||||
#undef CUDA_LIBNAME
|
||||
#undef NVCUVID_LIBNAME
|
||||
#undef NVENC_LIBNAME
|
||||
|
||||
#endif
|
||||
|
@ -0,0 +1,98 @@
|
||||
#include "mp4.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
uint32_t default_sample_size = 40000;
|
||||
|
||||
enum BufError create_header();
|
||||
|
||||
void set_sps(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t nal_len) {
|
||||
memcpy(ctx->buf_sps, nal_data, nal_len);
|
||||
ctx->buf_sps_len = nal_len;
|
||||
create_header(ctx);
|
||||
}
|
||||
|
||||
void set_pps(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t nal_len) {
|
||||
memcpy(ctx->buf_pps, nal_data, nal_len);
|
||||
ctx->buf_pps_len = nal_len;
|
||||
create_header(ctx);
|
||||
}
|
||||
|
||||
enum BufError create_header(struct Mp4Context *ctx) {
|
||||
if (ctx->buf_header.offset > 0) return BUF_OK;
|
||||
if (ctx->buf_sps_len == 0) return BUF_OK;
|
||||
if (ctx->buf_pps_len == 0) return BUF_OK;
|
||||
|
||||
struct MoovInfo moov_info;
|
||||
memset(&moov_info, 0, sizeof(struct MoovInfo));
|
||||
moov_info.width = ctx->w;
|
||||
moov_info.height = ctx->h;
|
||||
moov_info.horizontal_resolution = 0x00480000; // 72 dpi
|
||||
moov_info.vertical_resolution = 0x00480000; // 72 dpi
|
||||
moov_info.creation_time = 0;
|
||||
moov_info.timescale = default_sample_size * ctx->framerate;
|
||||
moov_info.sps = (uint8_t *) ctx->buf_sps;
|
||||
moov_info.sps_length = ctx->buf_sps_len;
|
||||
moov_info.pps = (uint8_t *) ctx->buf_pps;
|
||||
moov_info.pps_length = ctx->buf_pps_len;
|
||||
|
||||
ctx->buf_header.offset = 0;
|
||||
enum BufError err = write_header(&ctx->buf_header, &moov_info); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError get_header(struct Mp4Context *ctx, struct BitBuf *ptr) {
|
||||
ptr->buf = ctx->buf_header.buf;
|
||||
ptr->size = ctx->buf_header.size;
|
||||
ptr->offset = ctx->buf_header.offset;
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError set_slice(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t origlen, const uint32_t nal_len, const enum NalUnitType unit_type) {
|
||||
enum BufError err;
|
||||
|
||||
const uint32_t samples_info_len = 1;
|
||||
struct SampleInfo samples_info[1];
|
||||
memset(&samples_info[0], 0, sizeof(struct SampleInfo));
|
||||
samples_info[0].size = nal_len + 4; // add size of sample
|
||||
samples_info[0].composition_offset = default_sample_size;
|
||||
samples_info[0].decode_time = default_sample_size;
|
||||
samples_info[0].duration = default_sample_size;
|
||||
samples_info[0].flags = unit_type == NalUnitType_CodedSliceIdr ? 0 : 65536;
|
||||
|
||||
ctx->buf_moof.offset = 0;
|
||||
err = write_moof(&ctx->buf_moof, 0, 0, 0, default_sample_size, samples_info, samples_info_len); chk_err
|
||||
|
||||
ctx->buf_mdat.offset = 0;
|
||||
// printf("nal_data %d\n", nal_data);
|
||||
// printf(" slice: "); for (int i = 0; i < 32; ++i) printf(" 0x%02hhX", nal_data[i]); printf("\n");
|
||||
|
||||
err = write_mdat(&ctx->buf_mdat, nal_data, origlen, nal_len); chk_err
|
||||
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError set_mp4_state(struct Mp4Context *ctx, struct Mp4State *state) {
|
||||
enum BufError err = BUF_OK;
|
||||
if (pos_sequence_number > 0) err = put_u32_be_to_offset(&ctx->buf_moof, pos_sequence_number, state->sequence_number); chk_err
|
||||
if (pos_base_data_offset > 0) err = put_u64_be_to_offset(&ctx->buf_moof, pos_base_data_offset, state->base_data_offset); chk_err
|
||||
if (pos_base_media_decode_time > 0) err = put_u64_be_to_offset(&ctx->buf_moof, pos_base_media_decode_time, state->base_media_decode_time); chk_err
|
||||
state->sequence_number++;
|
||||
state->base_data_offset += ctx->buf_moof.offset + ctx->buf_mdat.offset;
|
||||
state->base_media_decode_time += state->default_sample_duration;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError get_moof(struct Mp4Context *ctx, struct BitBuf *ptr) {
|
||||
ptr->buf = ctx->buf_moof.buf;
|
||||
ptr->size = ctx->buf_moof.size;
|
||||
ptr->offset = ctx->buf_moof.offset;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError get_mdat(struct Mp4Context *ctx, struct BitBuf *ptr) {
|
||||
ptr->buf = ctx->buf_mdat.buf;
|
||||
ptr->size = ctx->buf_mdat.size;
|
||||
ptr->offset = ctx->buf_mdat.offset;
|
||||
// int o = 4+4;
|
||||
// printf(" get_mdat: "); for (int i = 0; i < 32; ++i) printf(" 0x%02hhX", ptr->buf[o+i]); printf("\n");
|
||||
return BUF_OK;
|
||||
}
|
@ -0,0 +1,197 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define chk_err if (err != BUF_OK) { printf("Error buf: %s %s(...) %s:%d\n", buf_error_to_str(err), __func__, __FILE__, __LINE__); return err; }
|
||||
#define chk_err_continue if (err != BUF_OK) { printf("Error buf: %s %s(...) %s:%d\n", buf_error_to_str(err), __func__, __FILE__, __LINE__); continue; }
|
||||
|
||||
enum BufError {
|
||||
BUF_OK = 0,
|
||||
BUF_ENDOFBUF_ERROR,
|
||||
BUF_MALLOC_ERROR,
|
||||
BUF_INCORRECT
|
||||
};
|
||||
char* buf_error_to_str(const enum BufError err);
|
||||
|
||||
struct BitBuf {
|
||||
char *buf;
|
||||
uint32_t size;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
enum BufError put_skip(struct BitBuf *ptr, const uint32_t count);
|
||||
enum BufError put_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint8_t* data, const uint32_t size);
|
||||
enum BufError put(struct BitBuf *ptr, const uint8_t* data, const uint32_t size);
|
||||
enum BufError put_u8_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint8_t val);
|
||||
enum BufError put_u8(struct BitBuf *ptr, uint8_t val);
|
||||
enum BufError put_u16_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint16_t val);
|
||||
enum BufError put_u16_be(struct BitBuf *ptr, const uint16_t val);
|
||||
enum BufError put_u16_le_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint16_t val);
|
||||
enum BufError put_u16_le(struct BitBuf *ptr, const uint16_t val);
|
||||
enum BufError put_u32_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint32_t val);
|
||||
enum BufError put_u32_be(struct BitBuf *ptr, const uint32_t val);
|
||||
enum BufError put_i32_be(struct BitBuf *ptr, const int32_t val);
|
||||
enum BufError put_u64_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint64_t val);
|
||||
enum BufError put_u64_be(struct BitBuf *ptr, const uint64_t val);
|
||||
enum BufError put_u32_le_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint32_t val);
|
||||
enum BufError put_u32_le(struct BitBuf *ptr, const uint32_t val);
|
||||
enum BufError put_str4_to_offset(struct BitBuf *ptr, const uint32_t offset, const char str[4]);
|
||||
enum BufError put_str4(struct BitBuf *ptr, const char str[4]);
|
||||
enum BufError put_counted_str_to_offset(struct BitBuf *ptr, const uint32_t offset, const char *str, const uint32_t len);
|
||||
enum BufError put_counted_str(struct BitBuf *ptr, const char *str, const uint32_t len);
|
||||
|
||||
struct MoovInfo {
|
||||
uint8_t profile_idc;
|
||||
uint8_t level_idc;
|
||||
uint8_t *sps;
|
||||
uint16_t sps_length;
|
||||
uint8_t *pps;
|
||||
uint16_t pps_length;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint32_t horizontal_resolution;
|
||||
uint32_t vertical_resolution;
|
||||
uint32_t creation_time;
|
||||
uint32_t timescale;
|
||||
};
|
||||
|
||||
enum BufError write_header(struct BitBuf *ptr, struct MoovInfo *moov_info);
|
||||
|
||||
extern uint32_t pos_sequence_number;
|
||||
extern uint32_t pos_base_data_offset;
|
||||
extern uint32_t pos_base_media_decode_time;
|
||||
|
||||
struct SampleInfo {
|
||||
uint32_t duration;
|
||||
uint32_t decode_time;
|
||||
uint32_t composition_time;
|
||||
uint32_t composition_offset;
|
||||
uint32_t size;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
enum BufError write_mdat(struct BitBuf *ptr, const uint8_t* data, const uint32_t origlen, const uint32_t len);
|
||||
enum BufError write_moof(struct BitBuf *ptr,
|
||||
const uint32_t sequence_number,
|
||||
const uint64_t base_data_offset,
|
||||
const uint64_t base_media_decode_time,
|
||||
const uint32_t default_sample_duration,
|
||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len);
|
||||
|
||||
enum NalUnitType { // Table 7-1 NAL unit type codes
|
||||
NalUnitType_Unspecified = 0, // Unspecified
|
||||
NalUnitType_CodedSliceNonIdr = 1, // Coded slice of a non-IDR picture
|
||||
NalUnitType_CodedSliceDataPartitionA = 2, // Coded slice data partition A
|
||||
NalUnitType_CodedSliceDataPartitionB = 3, // Coded slice data partition B
|
||||
NalUnitType_CodedSliceDataPartitionC = 4, // Coded slice data partition C
|
||||
NalUnitType_CodedSliceIdr = 5, // Coded slice of an IDR picture
|
||||
NalUnitType_SEI = 6, // Supplemental enhancement information (SEI)
|
||||
NalUnitType_SPS = 7, // Sequence parameter set
|
||||
NalUnitType_PPS = 8, // Picture parameter set
|
||||
NalUnitType_AUD = 9, // Access unit delimiter
|
||||
NalUnitType_EndOfSequence = 10, // End of sequence
|
||||
NalUnitType_EndOfStream = 11, // End of stream
|
||||
NalUnitType_Filler = 12, // Filler data
|
||||
NalUnitType_SpsExt = 13, // Sequence parameter set extension
|
||||
// 14..18 // Reserved
|
||||
NalUnitType_CodedSliceAux = 19, // Coded slice of an auxiliary coded picture without partitioning
|
||||
// 20..23 // Reserved
|
||||
// 24..31 // Unspecified
|
||||
};
|
||||
|
||||
struct NAL {
|
||||
char *data;
|
||||
uint64_t data_size;
|
||||
uint32_t picture_order_count;
|
||||
|
||||
// NAL header
|
||||
bool forbidden_zero_bit;
|
||||
uint8_t ref_idc;
|
||||
uint8_t unit_type_value;
|
||||
enum NalUnitType unit_type;
|
||||
};
|
||||
|
||||
static inline const char* nal_type_to_str(const enum NalUnitType nal_type) {
|
||||
switch (nal_type) {
|
||||
case NalUnitType_Unspecified: return "Unspecified";
|
||||
case NalUnitType_CodedSliceNonIdr: return "CodedSliceNonIdr";
|
||||
case NalUnitType_CodedSliceDataPartitionA: return "CodedSliceDataPartitionA";
|
||||
case NalUnitType_CodedSliceDataPartitionB: return "CodedSliceDataPartitionB";
|
||||
case NalUnitType_CodedSliceDataPartitionC: return "CodedSliceDataPartitionC";
|
||||
case NalUnitType_CodedSliceIdr: return "CodedSliceIdr";
|
||||
case NalUnitType_SEI: return "SEI";
|
||||
case NalUnitType_SPS: return "SPS";
|
||||
case NalUnitType_PPS: return "PPS";
|
||||
case NalUnitType_AUD: return "AUD";
|
||||
case NalUnitType_EndOfSequence: return "EndOfSequence";
|
||||
case NalUnitType_EndOfStream: return "EndOfStream";
|
||||
case NalUnitType_Filler: return "Filler";
|
||||
case NalUnitType_SpsExt: return "SpsExt";
|
||||
case NalUnitType_CodedSliceAux: return "CodedSliceAux";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static inline void nal_parse_header(struct NAL *nal, const char first_byte) {
|
||||
nal->forbidden_zero_bit = ((first_byte & 0x80) >> 7) == 1;
|
||||
nal->ref_idc = (first_byte & 0x60) >> 5;
|
||||
nal->unit_type = (enum NalUnitType) ((first_byte & 0x1f) >> 0);
|
||||
}
|
||||
|
||||
static inline bool nal_chk4(const uint8_t *buf, const uint32_t offset) {
|
||||
if (buf[offset] == 0x00 && buf[offset+1] == 0x00 && buf[offset+2] == 0x01) { return true; }
|
||||
if (buf[offset] == 0x00 && buf[offset+1] == 0x00 && buf[offset+2] == 0x00 && buf[offset+3] == 0x01) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool nal_chk3(const uint8_t *buf, const uint32_t offset) {
|
||||
if (buf[offset] == 0x00 && buf[offset+1] == 0x00 && buf[offset+2] == 0x01) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
extern uint32_t default_sample_size;
|
||||
|
||||
struct Mp4State {
|
||||
bool header_sent;
|
||||
|
||||
uint32_t sequence_number;
|
||||
uint64_t base_data_offset;
|
||||
uint64_t base_media_decode_time;
|
||||
uint32_t default_sample_duration;
|
||||
|
||||
uint32_t nals_count;
|
||||
};
|
||||
|
||||
struct Mp4Context {
|
||||
char buf_sps[128];
|
||||
uint16_t buf_sps_len;
|
||||
char buf_pps[128];
|
||||
uint16_t buf_pps_len;
|
||||
|
||||
uint16_t w, h, framerate;
|
||||
|
||||
struct BitBuf buf_header;
|
||||
struct BitBuf buf_moof;
|
||||
struct BitBuf buf_mdat;
|
||||
};
|
||||
|
||||
|
||||
enum BufError set_slice(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t origlen, const uint32_t nal_len, const enum NalUnitType unit_type);
|
||||
void set_sps(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t nal_len);
|
||||
void set_pps(struct Mp4Context *ctx, const uint8_t* nal_data, const uint32_t nal_len);
|
||||
|
||||
enum BufError get_header(struct Mp4Context *ctx, struct BitBuf *ptr);
|
||||
|
||||
enum BufError set_mp4_state(struct Mp4Context *ctx, struct Mp4State *state);
|
||||
enum BufError get_moof(struct Mp4Context *ctx, struct BitBuf *ptr);
|
||||
enum BufError get_mdat(struct Mp4Context *ctx, struct BitBuf *ptr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern C
|
||||
#endif
|
@ -0,0 +1,193 @@
|
||||
#include "mp4.h"
|
||||
|
||||
#define chk_ptr if (!ptr) return BUF_INCORRECT;
|
||||
#define chk_realloc { enum BufError err; err = try_to_realloc(ptr, pos); if (err != BUF_OK) return err; }
|
||||
|
||||
char* buf_error_to_str(const enum BufError err) {
|
||||
switch (err) {
|
||||
case BUF_OK: return "BUF_OK";
|
||||
case BUF_ENDOFBUF_ERROR: return "BUF_ENDOFBUF_ERROR";
|
||||
case BUF_MALLOC_ERROR: return "BUF_MALLOC_ERROR";
|
||||
case BUF_INCORRECT: return "BUF_INCORRECT";
|
||||
default: { static char str[32]; sprintf(str, "Unknown(%d)", err); return str; }
|
||||
}
|
||||
}
|
||||
|
||||
enum BufError try_to_realloc(struct BitBuf *ptr, const uint32_t min_size) {
|
||||
chk_ptr
|
||||
uint32_t new_size = ptr->size + min_size + 1024;
|
||||
char* new_buf = realloc(ptr->buf, new_size);
|
||||
if (new_buf == NULL) return BUF_MALLOC_ERROR;
|
||||
ptr->buf = new_buf;
|
||||
ptr->size = new_size;
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_skip(struct BitBuf *ptr, const uint32_t count) {
|
||||
chk_ptr
|
||||
uint32_t pos = ptr->offset + count;
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
for (uint32_t i = 0; i < count; i++) ptr->buf[ptr->offset + i] = 0;
|
||||
ptr->offset = pos;
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint8_t* data, const uint32_t size) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + size;
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
for (uint32_t i = 0; i < size; i++) ptr->buf[offset + i] = data[i];
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put(struct BitBuf *ptr, const uint8_t* data, const uint32_t size) {
|
||||
chk_ptr
|
||||
enum BufError err = put_to_offset(ptr, ptr->offset, data, size); chk_err
|
||||
ptr->offset += size;
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_u8_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint8_t val) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + sizeof(uint8_t);
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
ptr->buf[offset + 0] = val & 0xff;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_u8(struct BitBuf *ptr, uint8_t val) {
|
||||
chk_ptr
|
||||
enum BufError err = put_u8_to_offset(ptr, ptr->offset, val); chk_err
|
||||
ptr->offset += sizeof(uint8_t);
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_u16_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint16_t val) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + sizeof(uint16_t);
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
ptr->buf[offset + 0] = (val >> 8) & 0xff;
|
||||
ptr->buf[offset + 1] = (val >> 0) & 0xff;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_u16_be(struct BitBuf *ptr, const uint16_t val) {
|
||||
chk_ptr
|
||||
enum BufError err = put_u16_be_to_offset(ptr, ptr->offset, val); chk_err
|
||||
ptr->offset += sizeof(uint16_t);
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_u16_le_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint16_t val) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + sizeof(uint16_t);
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
ptr->buf[offset + 0] = (val >> 0) & 0xff;
|
||||
ptr->buf[offset + 1] = (val >> 8) & 0xff;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_u16_le(struct BitBuf *ptr, const uint16_t val) {
|
||||
chk_ptr
|
||||
enum BufError err = put_u16_le_to_offset(ptr, ptr->offset, val); chk_err
|
||||
ptr->offset += sizeof(uint16_t);
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_u32_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint32_t val) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + sizeof(uint32_t);
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
ptr->buf[offset + 0] = (val >> 24) & 0xff;
|
||||
ptr->buf[offset + 1] = (val >> 16) & 0xff;
|
||||
ptr->buf[offset + 2] = (val >> 8) & 0xff;
|
||||
ptr->buf[offset + 3] = (val >> 0) & 0xff;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_u32_be(struct BitBuf *ptr, const uint32_t val) {
|
||||
chk_ptr
|
||||
enum BufError err = put_u32_be_to_offset(ptr, ptr->offset, val); chk_err
|
||||
ptr->offset += sizeof(uint32_t);
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_i32_be(struct BitBuf *ptr, const int32_t val) {
|
||||
chk_ptr
|
||||
enum BufError err = put_u32_be_to_offset(ptr, ptr->offset, val); chk_err
|
||||
ptr->offset += sizeof(int32_t);
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_u64_be_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint64_t val) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + sizeof(uint64_t);
|
||||
if (pos > ptr->size) chk_realloc
|
||||
ptr->buf[offset + 0] = (val >> 56) & 0xff;
|
||||
ptr->buf[offset + 1] = (val >> 48) & 0xff;
|
||||
ptr->buf[offset + 2] = (val >> 40) & 0xff;
|
||||
ptr->buf[offset + 3] = (val >> 32) & 0xff;
|
||||
ptr->buf[offset + 4] = (val >> 24) & 0xff;
|
||||
ptr->buf[offset + 5] = (val >> 16) & 0xff;
|
||||
ptr->buf[offset + 6] = (val >> 8) & 0xff;
|
||||
ptr->buf[offset + 7] = (val >> 0) & 0xff;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_u64_be(struct BitBuf *ptr, const uint64_t val) {
|
||||
chk_ptr
|
||||
enum BufError err = put_u64_be_to_offset(ptr, ptr->offset, val); chk_err
|
||||
ptr->offset += sizeof(uint64_t);
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_u32_le_to_offset(struct BitBuf *ptr, const uint32_t offset, const uint32_t val) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + 4;
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
ptr->buf[offset + 0] = (val >> 0) & 0xff;
|
||||
ptr->buf[offset + 1] = (val >> 8) & 0xff;
|
||||
ptr->buf[offset + 2] = (val >> 16) & 0xff;
|
||||
ptr->buf[offset + 3] = (val >> 24) & 0xff;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_u32_le(struct BitBuf *ptr, const uint32_t val) {
|
||||
chk_ptr
|
||||
enum BufError err = put_u32_le_to_offset(ptr, ptr->offset, val); chk_err
|
||||
ptr->offset += sizeof(uint32_t);
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError put_str4_to_offset(struct BitBuf *ptr, const uint32_t offset, const char str[4]) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + 4;
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
for (uint8_t i = 0; i < 4; i++) { ptr->buf[offset + i] = str[i]; }
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_str4(struct BitBuf *ptr, const char str[4]) {
|
||||
chk_ptr
|
||||
enum BufError err = put_str4_to_offset(ptr, ptr->offset, str); chk_err
|
||||
ptr->offset += 4;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_counted_str_to_offset(struct BitBuf *ptr, const uint32_t offset, const char *str, const uint32_t len) {
|
||||
chk_ptr
|
||||
uint32_t pos = offset + len + 1;
|
||||
if (pos >= ptr->size) chk_realloc
|
||||
for (uint32_t i = 0; i < len+1; i++) { ptr->buf[offset + i] = str[i]; }
|
||||
ptr->buf[pos] = 0;
|
||||
return BUF_OK;
|
||||
}
|
||||
enum BufError put_counted_str(struct BitBuf *ptr, const char *str, const uint32_t len) {
|
||||
chk_ptr
|
||||
enum BufError err = put_counted_str_to_offset(ptr, ptr->offset, str, len); chk_err
|
||||
ptr->offset += len+1;
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
// enum BufError hexStr(struct BitBuf *ptr, char *str) : string {
|
||||
// let str = '';
|
||||
// for(let i = 0; i < this.offset; i++) {
|
||||
// if (i % 40 === 0) str += '\n';
|
||||
// if (i % 4 === 0 && i+1 < this.data.length) {
|
||||
// if (i > 0 && i % 40 !== 0) str += ' ';
|
||||
// str += '0x';
|
||||
// }
|
||||
// str += decimalToHex(this.data[i]);
|
||||
// }
|
||||
// return str;
|
||||
// }
|
@ -0,0 +1,194 @@
|
||||
#include "mp4.h"
|
||||
#include <string.h>
|
||||
|
||||
uint32_t pos_sequence_number = 0;
|
||||
uint32_t pos_base_data_offset = 0;
|
||||
uint32_t pos_base_media_decode_time = 0;
|
||||
|
||||
struct DataOffsetPos {
|
||||
bool data_offset_present;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
enum BufError write_mfhd(struct BitBuf *ptr, const uint32_t sequence_number);
|
||||
enum BufError write_traf(struct BitBuf *ptr,
|
||||
const uint32_t sequence_number,
|
||||
const uint64_t base_data_offset,
|
||||
const uint64_t base_media_decode_time,
|
||||
const uint32_t default_sample_duration,
|
||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len,
|
||||
struct DataOffsetPos *data_offset);
|
||||
enum BufError write_tfhd(struct BitBuf *ptr,
|
||||
const uint32_t sequence_number,
|
||||
const uint64_t base_data_offset,
|
||||
const uint64_t base_media_decode_time,
|
||||
const uint32_t default_sample_size,
|
||||
const uint32_t default_sample_duration,
|
||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len,
|
||||
struct DataOffsetPos *data_offset);
|
||||
enum BufError write_tfdt(struct BitBuf *ptr, const uint64_t base_media_decode_time);
|
||||
enum BufError write_trun(struct BitBuf *ptr,
|
||||
const struct SampleInfo *samples_info, const uint32_t samples_info_count,
|
||||
struct DataOffsetPos *data_offset);
|
||||
|
||||
enum BufError write_mdat(struct BitBuf *ptr, const uint8_t* data, const uint32_t origlen, const uint32_t len) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mdat"); chk_err
|
||||
err = put_u32_be(ptr, origlen); chk_err // todo
|
||||
// printf(" write_mdat: "); for (int i = 0; i < 32; ++i) printf(" 0x%02hhX", data[i]); printf("\n");
|
||||
err = put(ptr, data, len); chk_err
|
||||
// printf("mdat len %d ptr->offset %d start_atom: %d \n", len, ptr->offset, start_atom);
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_moof(struct BitBuf *ptr,
|
||||
const uint32_t sequence_number,
|
||||
const uint64_t base_data_offset,
|
||||
const uint64_t base_media_decode_time,
|
||||
const uint32_t default_sample_duration,
|
||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len)
|
||||
{
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "moof"); chk_err
|
||||
err = write_mfhd(ptr, sequence_number); chk_err
|
||||
struct DataOffsetPos data_offset;
|
||||
data_offset.offset = 0;
|
||||
err = write_traf(ptr, sequence_number, base_data_offset, base_media_decode_time, default_sample_duration, samples_info, samples_info_len, &data_offset); chk_err
|
||||
if (data_offset.data_offset_present)
|
||||
err = put_u32_be_to_offset(ptr, data_offset.offset, ptr->offset + 4 /*mdat size*/ + 4 /*mdat id*/); chk_err
|
||||
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_mfhd(struct BitBuf *ptr, const uint32_t sequence_number) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mfhd"); chk_err
|
||||
err = put_u8(ptr, 0); // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
pos_sequence_number = ptr->offset;
|
||||
err = put_u32_be(ptr, sequence_number); chk_err // 4 sequence_number
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_traf(struct BitBuf *ptr,
|
||||
const uint32_t sequence_number,
|
||||
const uint64_t base_data_offset,
|
||||
const uint64_t base_media_decode_time,
|
||||
const uint32_t default_sample_duration,
|
||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len,
|
||||
struct DataOffsetPos *data_offset)
|
||||
{
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "traf"); chk_err
|
||||
err = write_tfhd(ptr, sequence_number, base_data_offset, base_media_decode_time, samples_info[0].size, default_sample_duration, samples_info, samples_info_len, data_offset); chk_err
|
||||
err = write_tfdt(ptr, base_media_decode_time); chk_err
|
||||
err = write_trun(ptr, samples_info, samples_info_len, data_offset); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_tfhd(struct BitBuf *ptr,
|
||||
const uint32_t sequence_number,
|
||||
const uint64_t base_data_offset,
|
||||
const uint64_t base_media_decode_time,
|
||||
const uint32_t default_sample_size,
|
||||
const uint32_t default_sample_duration,
|
||||
const struct SampleInfo *samples_info, const uint32_t samples_info_len,
|
||||
struct DataOffsetPos *data_offset)
|
||||
{
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "tfhd"); chk_err
|
||||
|
||||
err = put_u8(ptr, 0); chk_err // 1 byte version
|
||||
uint64_t flags = 0x0;
|
||||
const bool base_data_offset_present = false;
|
||||
const bool sample_description_index_present = false;
|
||||
const bool default_sample_duration_present = true;
|
||||
const bool default_sample_size_present = true;
|
||||
const bool default_sample_flags_present = true;
|
||||
const bool duration_is_empty = false;
|
||||
const bool default_base_is_moof = false;
|
||||
|
||||
if (base_data_offset_present) { flags = flags | 0x000001; } // base-data-offset-present
|
||||
if (sample_description_index_present) { flags = flags | 0x000002; } // sample-description-index-present
|
||||
if (default_sample_duration_present) { flags = flags | 0x000008; } // default-sample-duration-present
|
||||
if (default_sample_size_present) { flags = flags | 0x000010; } // default-sample-size-present
|
||||
if (default_sample_flags_present) { flags = flags | 0x000020; } // default-sample-flags-present
|
||||
if (duration_is_empty) { flags = flags | 0x010000; } // duration-is-empty
|
||||
if (default_base_is_moof) { flags = flags | 0x020000; } // default-base-is-moof
|
||||
// buf.put_u8(0); buf.put_u8(0); buf.put_u8(0x39); // 3 flags
|
||||
// println!("tfhd flags: 0x{:06x} 0x{:02x}: 0x{:02x}: 0x{:02x}", flags, (flags >> 16) as u8, (flags >> 8) as u8, (flags >> 0) as u8);
|
||||
err = put_u8(ptr, flags >> 16); chk_err; err = put_u8(ptr, flags >> 8); chk_err; err = put_u8(ptr, flags >> 0); chk_err // 3 flags
|
||||
|
||||
err = put_u32_be(ptr, 1); chk_err // 4 track_ID
|
||||
if (base_data_offset_present) { pos_base_data_offset = ptr->offset; err = put_u64_be(ptr, base_data_offset); chk_err }
|
||||
// if sample_description_index_present { buf.put_u32_be(0); } // 4 default_sample_description_index
|
||||
if (default_sample_duration_present) { err = put_u32_be(ptr, default_sample_duration); chk_err }
|
||||
if (default_sample_size_present) { err = put_u32_be(ptr, default_sample_size); chk_err }
|
||||
if (default_sample_flags_present) { err = put_u32_be(ptr, 16842752); chk_err }
|
||||
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_tfdt(struct BitBuf *ptr, const uint64_t base_media_decode_time) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "tfdt"); chk_err
|
||||
err = put_u8(ptr, 1); // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
pos_base_media_decode_time = ptr->offset;
|
||||
err = put_u64_be(ptr, base_media_decode_time); chk_err // 4 baseMediaDecodeTime
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_trun(struct BitBuf *ptr,
|
||||
const struct SampleInfo *samples_info, const uint32_t samples_info_count,
|
||||
struct DataOffsetPos *data_offset)
|
||||
{
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "trun"); chk_err
|
||||
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
const bool data_offset_present = true;
|
||||
const bool first_sample_flags_present = false;
|
||||
const bool sample_duration_present = true;
|
||||
const bool sample_size_present = true;
|
||||
const bool sample_flags_present = true;
|
||||
const bool sample_composition_time_offsets_present = true;
|
||||
{
|
||||
uint64_t flags = 0x0;
|
||||
if (data_offset_present) { flags = flags | 0x000001; } // 0x000001 data-offset-present.
|
||||
if (first_sample_flags_present) { flags = flags | 0x000004; } // 0x000004 first-sample-flags-present
|
||||
if (sample_duration_present) { flags = flags | 0x000100; } // 0x000100 sample-duration-present
|
||||
if (sample_size_present) { flags = flags | 0x000200; } // 0x000200 sample-size-present
|
||||
if (sample_flags_present) { flags = flags | 0x000400; } // 0x000400 sample-flags-present
|
||||
if (sample_composition_time_offsets_present) { flags = flags | 0x000800; } // 0x000800 sample-composition-time-offsets-present
|
||||
// println!("trup flags: 0x{:06x} 0x{:02x}: 0x{:02x}: 0x{:02x}", flags, (flags >> 16) as u8, (flags >> 8) as u8, (flags >> 0) as u8);
|
||||
err = put_u8(ptr, flags >> 16); chk_err; err = put_u8(ptr, flags >> 8); chk_err; err = put_u8(ptr, flags >> 0); chk_err // 3 flags
|
||||
}
|
||||
err = put_u32_be(ptr, samples_info_count); chk_err // 4 sample_count
|
||||
|
||||
data_offset->data_offset_present = data_offset_present;
|
||||
data_offset->offset = ptr->offset; // save pointer to this place. we will change size after moof atom will created
|
||||
if (data_offset_present) { err = put_i32_be(ptr, 0); chk_err } // 4 fake data_offset
|
||||
|
||||
if (first_sample_flags_present) { err = put_u32_be(ptr, 33554432); chk_err } // 4 first_sample_flags
|
||||
for (uint32_t i = 0; i < samples_info_count; ++i) {
|
||||
const struct SampleInfo sample_info = samples_info[i];
|
||||
if (sample_duration_present) { err = put_u32_be(ptr, sample_info.duration); chk_err } // 4 sample_duration
|
||||
if (sample_size_present) { err = put_u32_be(ptr, sample_info.size); chk_err } // 4 sample_size
|
||||
if (sample_flags_present) { err = put_u32_be(ptr, sample_info.flags); chk_err } // 4 sample_flags
|
||||
if (sample_composition_time_offsets_present) {
|
||||
// if version == 0 { err = put_u32_be(ptr, sample_info.composition_offset as u32); chk_err }
|
||||
// else
|
||||
{ err = put_i32_be(ptr, sample_info.composition_offset); chk_err }
|
||||
}
|
||||
}
|
||||
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
@ -0,0 +1,420 @@
|
||||
#include "mp4.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
enum BufError write_ftyp(struct BitBuf *ptr);
|
||||
enum BufError write_moov(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
|
||||
enum BufError write_mvhd(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_trak(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_tkhd(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_mdia(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_mdhd(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_minf(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_dinf(struct BitBuf *ptr);
|
||||
enum BufError write_dref(struct BitBuf *ptr);
|
||||
enum BufError write_url(struct BitBuf *ptr);
|
||||
enum BufError write_vmhd(struct BitBuf *ptr);
|
||||
enum BufError write_stbl(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_stsd(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_avc1(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_avcC(struct BitBuf *ptr, const struct MoovInfo *moov_info);
|
||||
enum BufError write_stts(struct BitBuf *ptr);
|
||||
enum BufError write_stsc(struct BitBuf *ptr);
|
||||
enum BufError write_stsz(struct BitBuf *ptr);
|
||||
enum BufError write_stco(struct BitBuf *ptr);
|
||||
enum BufError write_mvex(struct BitBuf *ptr);
|
||||
enum BufError write_trex(struct BitBuf *ptr);
|
||||
enum BufError write_udta(struct BitBuf *ptr);
|
||||
enum BufError write_meta(struct BitBuf *ptr);
|
||||
enum BufError write_hdlr(struct BitBuf *ptr, const char name[4], const char manufacturer[4], const char *value, const uint32_t value_len);
|
||||
enum BufError write_ilst(struct BitBuf *ptr, const uint8_t *array, const uint32_t len);
|
||||
|
||||
enum BufError write_header(struct BitBuf *ptr, struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
err = write_ftyp(ptr); chk_err
|
||||
err = write_moov(ptr, moov_info); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_ftyp(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
// atom header <fake size><id>
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "ftyp"); chk_err
|
||||
|
||||
err = put_str4(ptr, "isom"); chk_err // major_brand
|
||||
err = put_u32_be(ptr, 0x00000200); chk_err // minor_version
|
||||
err = put_str4(ptr, "isom"); chk_err
|
||||
err = put_str4(ptr, "iso2"); chk_err
|
||||
err = put_str4(ptr, "avc1"); chk_err
|
||||
err = put_str4(ptr, "iso6"); chk_err
|
||||
err = put_str4(ptr, "mp41"); chk_err
|
||||
|
||||
// write atom size
|
||||
uint32_t atom_size = ptr->offset - start_atom;
|
||||
err = put_u32_be_to_offset(ptr, start_atom, atom_size); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
|
||||
enum BufError write_moov(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "moov"); chk_err
|
||||
err = write_mvhd(ptr, moov_info); chk_err
|
||||
err = write_trak(ptr, moov_info); chk_err
|
||||
err = write_mvex(ptr); chk_err
|
||||
err = write_udta(ptr); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
|
||||
enum BufError write_mvhd(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mvhd"); chk_err
|
||||
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err=put_u8(ptr, 0);chk_err; err=put_u8(ptr, 0);chk_err // 3 flags
|
||||
err = put_u32_be(ptr, moov_info->creation_time); chk_err // 4 creation_time
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 modification_time
|
||||
err = put_u32_be(ptr, moov_info->timescale); chk_err // 4 timescale
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 duration
|
||||
err = put_u32_be(ptr, 65536); chk_err // 4 preferred rate
|
||||
err = put_u16_le(ptr, 1); chk_err // 2 preferred volume
|
||||
err = put_skip(ptr, 10); chk_err // 10 reserved
|
||||
{ // 36 matrix
|
||||
err = put_u32_be(ptr, 65536); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 65536); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 1073741824); chk_err
|
||||
}
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Preview time
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Preview duration
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Poster time
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Selection time
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Selection duration
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Current time
|
||||
err = put_u32_be(ptr, 2); chk_err // 4 Next track ID
|
||||
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_trak(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "trak"); chk_err
|
||||
err = write_tkhd(ptr, moov_info); chk_err
|
||||
err = write_mdia(ptr, moov_info); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_tkhd(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "tkhd"); chk_err
|
||||
|
||||
err = put_u8(ptr, 0); // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 3); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, moov_info->creation_time); // 4 creation_time
|
||||
err = put_u32_be(ptr, 0); // 4 modification_time
|
||||
err = put_u32_be(ptr, 1); // 4 track id
|
||||
err = put_u32_be(ptr, 0); // 4 reserved
|
||||
err = put_u32_be(ptr, 0); // 4 duration
|
||||
err = put_skip(ptr, 8); // 8 reserved
|
||||
err = put_u16_be(ptr, 0); // 2 layer
|
||||
err = put_u16_be(ptr, 0); // 2 Alternate group
|
||||
err = put_u16_be(ptr, 0); // 2 Volume
|
||||
err = put_u16_be(ptr, 0); // 2 Reserved
|
||||
{ // 36 Matrix structure
|
||||
err = put_u32_be(ptr, 65536); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 65536); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 1073741824); chk_err
|
||||
}
|
||||
err = put_u32_be(ptr, moov_info->width * 65536); chk_err // 4 Track width
|
||||
err = put_u32_be(ptr, moov_info->height * 65536); chk_err // 4 Track height
|
||||
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
|
||||
enum BufError write_mdia(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mdia"); chk_err
|
||||
err = write_mdhd(ptr, moov_info); chk_err
|
||||
char *str = "VideoHandler";
|
||||
err = write_hdlr(ptr, "vide", "\0\0\0\0", str, strlen(str)); chk_err
|
||||
err = write_minf(ptr, moov_info); chk_err
|
||||
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_mdhd(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mdhd"); chk_err
|
||||
err = put_u8(ptr, 0); // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 creation_time
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 modification_time
|
||||
err = put_u32_be(ptr, moov_info->timescale); chk_err // 4 timescale
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 duration
|
||||
err = put_u16_be(ptr, 21956); chk_err // 2 language
|
||||
err = put_u16_be(ptr, 0); chk_err // 2 quality
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_minf(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "minf"); chk_err
|
||||
err = write_vmhd(ptr); chk_err
|
||||
err = write_dinf(ptr); chk_err
|
||||
err = write_stbl(ptr, moov_info); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_dinf(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "dinf"); chk_err
|
||||
err = write_dref(ptr); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_dref(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "dref"); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 1); chk_err // 4 Component flags mask
|
||||
err = write_url(ptr); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_url(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "url "); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 1); chk_err // 3 flags
|
||||
//err = put_u8(ptr, 0); chk_err // <counted string> end
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_vmhd(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "vmhd"); chk_err
|
||||
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 1); chk_err // 3 flags
|
||||
err = put_u16_be(ptr, 0); chk_err // 2 Graphics mode
|
||||
err = put_u16_be(ptr, 0); chk_err // 2 Opcolor
|
||||
err = put_u16_be(ptr, 0); chk_err // 2 Opcolor
|
||||
err = put_u16_be(ptr, 0); chk_err // 2 Opcolor
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_stbl(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stbl"); chk_err
|
||||
err = write_stsd(ptr, moov_info); chk_err
|
||||
err = write_stts(ptr); chk_err
|
||||
err = write_stsc(ptr); chk_err
|
||||
err = write_stsz(ptr); chk_err
|
||||
err = write_stco(ptr); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_stsd(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stsd"); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 1); chk_err // 4 Number of entries
|
||||
err = write_avc1(ptr, moov_info); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_avc1(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "avc1"); chk_err
|
||||
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // reserved
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // reserved
|
||||
err = put_u16_be(ptr, 1); chk_err // data_reference_index
|
||||
err = put_u16_be(ptr, 0); chk_err // pre_defined
|
||||
err = put_u16_be(ptr, 0); chk_err // reserved
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err
|
||||
err = put_u32_be(ptr, 0); chk_err // pre_defined
|
||||
err = put_u16_be(ptr, moov_info->width); chk_err // 2 width
|
||||
err = put_u16_be(ptr, moov_info->height); chk_err // 2 height
|
||||
err = put_u32_be(ptr, moov_info->horizontal_resolution); chk_err // 4 horizontal_resolution
|
||||
err = put_u32_be(ptr, moov_info->vertical_resolution); chk_err // 4 vertical_resolution
|
||||
err = put_u32_be(ptr, 0); chk_err // reserved
|
||||
err = put_u16_be(ptr, 1); chk_err // 2 frame_count
|
||||
err = put_u8(ptr, 0); chk_err
|
||||
// uint8_t *compressorname = { 0, 0, 0, 0, // dailymotion/hls.js
|
||||
// 0, 0, 0, 0,
|
||||
// 0, 0, 0, 0,
|
||||
// 0, 0, 0, 0,
|
||||
// 0, 0, 0, 0,
|
||||
// 0, 0, 0, 0,
|
||||
// 0, 0, 0, 0,
|
||||
// 0, 0, 0 };
|
||||
char compressorname[50] = "OpenIPC project ";
|
||||
|
||||
err = put(ptr, (uint8_t *) compressorname, 31); chk_err // compressorname
|
||||
err = put_u16_be(ptr, 24); chk_err // 2 depth
|
||||
err = put_u16_be(ptr, 0xffff); chk_err // 2 color_table_id
|
||||
err = write_avcC(ptr, moov_info); chk_err
|
||||
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
|
||||
enum BufError write_avcC(struct BitBuf *ptr, const struct MoovInfo *moov_info) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "avcC"); chk_err
|
||||
|
||||
err = put_u8(ptr, 1); chk_err // 1 version
|
||||
err = put_u8(ptr, moov_info->sps[1]); chk_err // 1 profile
|
||||
err = put_u8(ptr, moov_info->sps[2]); chk_err // 1 compatibility
|
||||
err = put_u8(ptr, moov_info->sps[3]); chk_err // 1 level
|
||||
err = put_u8(ptr, 0xFF); chk_err // 6 bits reserved (111111) + 2 bits nal size length - 1 (11)
|
||||
err = put_u8(ptr, 0xE1); chk_err // 3 bits reserved (111) + 5 bits number of sps (00001)
|
||||
err = put_u16_be(ptr, moov_info->sps_length); chk_err
|
||||
err = put(ptr, (const uint8_t *) moov_info->sps, moov_info->sps_length); chk_err // SPS
|
||||
err = put_u8(ptr, 1); chk_err // 1 num pps
|
||||
err = put_u16_be(ptr, moov_info->pps_length); chk_err
|
||||
err = put(ptr, (const uint8_t *)moov_info->pps, moov_info->pps_length); chk_err // pps
|
||||
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_stts(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stts"); chk_err
|
||||
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 0); chk_err // Number of entries
|
||||
// Time-to-sample table
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_stsc(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stsc"); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 0); chk_err // Number of entries
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_stsz(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stsz"); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 0); chk_err // Sample size
|
||||
err = put_u32_be(ptr, 0); chk_err // Number of entries
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_stco(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "stco"); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 0); chk_err // Number of entries
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_mvex(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "mvex"); chk_err
|
||||
err = write_trex(ptr); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_trex(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "trex"); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 1); chk_err // track_ID
|
||||
err = put_u32_be(ptr, 1); chk_err // default_sample_description_index
|
||||
err = put_u32_be(ptr, 0); chk_err // default_sample_duration
|
||||
err = put_u32_be(ptr, 0); chk_err // default_sample_size
|
||||
err = put_u32_be(ptr, 0); chk_err // default_sample_flags
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_udta(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "udta"); chk_err
|
||||
err = write_meta(ptr); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_meta(struct BitBuf *ptr) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "meta"); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = write_hdlr(ptr, "mdir", "appl", "", 0); chk_err
|
||||
uint8_t array[37] = {0,0,0,37,169,116,111,111,0,0,0,29,100,97,116,97,0,0,0,1,0,0,0,0,76,97,118,102,53,55,46,56,51,46,49,48,48};
|
||||
err = write_ilst(ptr, array, 37); chk_err
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_hdlr(struct BitBuf *ptr, const char name[4], const char manufacturer[4], const char *value, const uint32_t value_len) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "hdlr"); chk_err
|
||||
err = put_u8(ptr, 0); chk_err // 1 version
|
||||
err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err; err = put_u8(ptr, 0); chk_err // 3 flags
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Predefined
|
||||
err = put_str4(ptr, name); chk_err // 4 Component subtype
|
||||
err = put_str4(ptr, manufacturer); chk_err // 4 Component manufacturer
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Component flags
|
||||
err = put_u32_be(ptr, 0); chk_err // 4 Component flags mask
|
||||
err = put_counted_str(ptr, value, value_len); chk_err // <counted string> Component name
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
||||
|
||||
enum BufError write_ilst(struct BitBuf *ptr, const uint8_t *array, const uint32_t len) {
|
||||
enum BufError err;
|
||||
uint32_t start_atom = ptr->offset; err = put_u32_be(ptr, 0); chk_err; err = put_str4(ptr, "ilst"); chk_err
|
||||
err = put(ptr, array, len); chk_err // <counted string> Component name
|
||||
err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err
|
||||
return BUF_OK;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,453 @@
|
||||
/*
|
||||
* H.264/HEVC hardware encoding using nvidia nvenc
|
||||
* Copyright (c) 2016 Timo Rothenpieler <timo@rothenpieler.org>
|
||||
*
|
||||
* This file is part of FFmpeg.
|
||||
*
|
||||
* FFmpeg is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* FFmpeg 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with FFmpeg; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <rfb/LogWriter.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nvidia.h"
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
static LogWriter vlog("nvidia");
|
||||
|
||||
#define FFNV_LOG_FUNC(logctx, msg, ...) vlog.info((msg), __VA_ARGS__)
|
||||
#define FFNV_DEBUG_LOG_FUNC(logctx, msg, ...)
|
||||
|
||||
#include "dynlink_loader.h"
|
||||
|
||||
#define NUM_SURF 4
|
||||
|
||||
typedef struct NvencSurface
|
||||
{
|
||||
NV_ENC_INPUT_PTR input_surface;
|
||||
int reg_idx;
|
||||
int width;
|
||||
int height;
|
||||
int pitch;
|
||||
|
||||
NV_ENC_OUTPUT_PTR output_surface;
|
||||
NV_ENC_BUFFER_FORMAT format;
|
||||
} NvencSurface;
|
||||
|
||||
typedef struct NvencDynLoadFunctions
|
||||
{
|
||||
CudaFunctions *cuda_dl;
|
||||
NvencFunctions *nvenc_dl;
|
||||
|
||||
void *nvenc_ctx;
|
||||
NV_ENCODE_API_FUNCTION_LIST nvenc_funcs;
|
||||
|
||||
NV_ENC_INITIALIZE_PARAMS init_enc_parms;
|
||||
NV_ENC_CONFIG enc_cfg;
|
||||
CUdevice cu_dev;
|
||||
CUcontext cu_ctx;
|
||||
|
||||
NvencSurface surf[NUM_SURF];
|
||||
uint8_t cursurf;
|
||||
} NvencDynLoadFunctions;
|
||||
|
||||
static NvencDynLoadFunctions nvenc;
|
||||
|
||||
/*
|
||||
Recommended settings for streaming
|
||||
Low-Latency High Quality preset
|
||||
Rate control mode = Two-pass CBR
|
||||
Very low VBV buffer size (Single frame)
|
||||
No B Frames
|
||||
Infinite GOP length
|
||||
Adaptive Quantization enabled
|
||||
*/
|
||||
|
||||
static int loadfuncs() {
|
||||
int ret;
|
||||
NVENCSTATUS err;
|
||||
uint32_t nvenc_max_ver;
|
||||
|
||||
ret = cuda_load_functions(&nvenc.cuda_dl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = nvenc_load_functions(&nvenc.nvenc_dl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
err = nvenc.nvenc_dl->NvEncodeAPIGetMaxSupportedVersion(&nvenc_max_ver);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
vlog.info("Loaded nvenc version %u.%u", nvenc_max_ver >> 4, nvenc_max_ver & 0xf);
|
||||
|
||||
if ((NVENCAPI_MAJOR_VERSION << 4 | NVENCAPI_MINOR_VERSION) > nvenc_max_ver) {
|
||||
vlog.error("Your Nvidia driver is too old. Nvenc %u.%u required",
|
||||
NVENCAPI_MAJOR_VERSION, NVENCAPI_MINOR_VERSION);
|
||||
return -1;
|
||||
}
|
||||
|
||||
nvenc.nvenc_funcs.version = NV_ENCODE_API_FUNCTION_LIST_VER;
|
||||
|
||||
err = nvenc.nvenc_dl->NvEncodeAPICreateInstance(&nvenc.nvenc_funcs);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvenc_check_cap(NV_ENC_CAPS cap) {
|
||||
NV_ENC_CAPS_PARAM params;
|
||||
memset(¶ms, 0, sizeof(NV_ENC_CAPS_PARAM));
|
||||
|
||||
params.version = NV_ENC_CAPS_PARAM_VER;
|
||||
params.capsToQuery = cap;
|
||||
|
||||
int ret, val = 0;
|
||||
|
||||
ret = nvenc.nvenc_funcs.nvEncGetEncodeCaps(nvenc.nvenc_ctx,
|
||||
nvenc.init_enc_parms.encodeGUID,
|
||||
¶ms, &val);
|
||||
if (ret == NV_ENC_SUCCESS)
|
||||
return val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setupdevice() {
|
||||
int ret;
|
||||
|
||||
nvenc.init_enc_parms.encodeGUID = NV_ENC_CODEC_H264_GUID;
|
||||
nvenc.init_enc_parms.presetGUID = NV_ENC_PRESET_P7_GUID;
|
||||
|
||||
ret = nvenc.cuda_dl->cuInit(0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = nvenc.cuda_dl->cuDeviceGet(&nvenc.cu_dev, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = nvenc.cuda_dl->cuCtxCreate(&nvenc.cu_ctx, CU_CTX_SCHED_BLOCKING_SYNC,
|
||||
nvenc.cu_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
CUcontext dummy;
|
||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
||||
|
||||
// cuda stream is NULL to use the default
|
||||
|
||||
// open session
|
||||
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params;
|
||||
memset(¶ms, 0, sizeof(NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS));
|
||||
NVENCSTATUS err;
|
||||
|
||||
params.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER;
|
||||
params.apiVersion = NVENCAPI_VERSION;
|
||||
params.device = nvenc.cu_ctx;
|
||||
params.deviceType = NV_ENC_DEVICE_TYPE_CUDA;
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncOpenEncodeSessionEx(¶ms, &nvenc.nvenc_ctx);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
// check caps
|
||||
const int maxw = nvenc_check_cap(NV_ENC_CAPS_WIDTH_MAX);
|
||||
const int maxh = nvenc_check_cap(NV_ENC_CAPS_HEIGHT_MAX);
|
||||
const int minw = nvenc_check_cap(NV_ENC_CAPS_WIDTH_MIN);
|
||||
const int minh = nvenc_check_cap(NV_ENC_CAPS_HEIGHT_MIN);
|
||||
|
||||
vlog.info("Max enc resolution %ux%u, min %ux%u", maxw, maxh, minw, minh);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setupenc(const unsigned w, const unsigned h, const unsigned kbps,
|
||||
const unsigned fps) {
|
||||
NVENCSTATUS err;
|
||||
|
||||
nvenc.enc_cfg.version = NV_ENC_CONFIG_VER;
|
||||
nvenc.init_enc_parms.version = NV_ENC_INITIALIZE_PARAMS_VER;
|
||||
nvenc.init_enc_parms.darWidth =
|
||||
nvenc.init_enc_parms.encodeWidth = w;
|
||||
nvenc.init_enc_parms.darHeight =
|
||||
nvenc.init_enc_parms.encodeHeight = h;
|
||||
|
||||
nvenc.init_enc_parms.frameRateNum = fps;
|
||||
nvenc.init_enc_parms.frameRateDen = 1;
|
||||
|
||||
nvenc.init_enc_parms.encodeConfig = &nvenc.enc_cfg;
|
||||
nvenc.init_enc_parms.tuningInfo = NV_ENC_TUNING_INFO_LOW_LATENCY;
|
||||
|
||||
NV_ENC_PRESET_CONFIG preset_cfg;
|
||||
memset(&preset_cfg, 0, sizeof(NV_ENC_PRESET_CONFIG));
|
||||
|
||||
preset_cfg.version = NV_ENC_PRESET_CONFIG_VER;
|
||||
preset_cfg.presetCfg.version = NV_ENC_CONFIG_VER;
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncGetEncodePresetConfigEx(nvenc.nvenc_ctx,
|
||||
nvenc.init_enc_parms.encodeGUID,
|
||||
nvenc.init_enc_parms.presetGUID,
|
||||
nvenc.init_enc_parms.tuningInfo,
|
||||
&preset_cfg);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
memcpy(&nvenc.enc_cfg, &preset_cfg.presetCfg, sizeof(nvenc.enc_cfg));
|
||||
|
||||
nvenc.enc_cfg.version = NV_ENC_CONFIG_VER;
|
||||
|
||||
nvenc.init_enc_parms.enableEncodeAsync = 0;
|
||||
nvenc.init_enc_parms.enablePTD = 1;
|
||||
|
||||
nvenc.enc_cfg.frameIntervalP = 0;
|
||||
nvenc.enc_cfg.gopLength = 1;
|
||||
|
||||
// use 4 surfaces
|
||||
|
||||
// setup rate control
|
||||
nvenc.enc_cfg.rcParams.multiPass = NV_ENC_TWO_PASS_FULL_RESOLUTION;
|
||||
nvenc.enc_cfg.rcParams.averageBitRate = kbps * 1024;
|
||||
nvenc.enc_cfg.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR;
|
||||
nvenc.enc_cfg.rcParams.lowDelayKeyFrameScale = 1;
|
||||
|
||||
nvenc.enc_cfg.rcParams.enableAQ = 1;
|
||||
nvenc.enc_cfg.rcParams.aqStrength = 4; // 1 - 15, 0 would be auto
|
||||
|
||||
nvenc.enc_cfg.frameFieldMode = NV_ENC_PARAMS_FRAME_FIELD_MODE_FRAME;
|
||||
|
||||
// setup_codec_config
|
||||
nvenc.enc_cfg.encodeCodecConfig.h264Config.h264VUIParameters.videoFullRangeFlag = 1;
|
||||
nvenc.enc_cfg.encodeCodecConfig.h264Config.outputBufferingPeriodSEI = 1;
|
||||
nvenc.enc_cfg.encodeCodecConfig.h264Config.adaptiveTransformMode = NV_ENC_H264_ADAPTIVE_TRANSFORM_ENABLE;
|
||||
nvenc.enc_cfg.encodeCodecConfig.h264Config.fmoMode = NV_ENC_H264_FMO_DISABLE;
|
||||
nvenc.enc_cfg.profileGUID = NV_ENC_H264_PROFILE_MAIN_GUID;
|
||||
|
||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncInitializeEncoder(nvenc.nvenc_ctx,
|
||||
&nvenc.init_enc_parms);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
// custream?
|
||||
|
||||
CUcontext dummy;
|
||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setupsurf(const unsigned w, const unsigned h) {
|
||||
|
||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < NUM_SURF; i++) {
|
||||
NVENCSTATUS err;
|
||||
NV_ENC_CREATE_BITSTREAM_BUFFER allocOut;
|
||||
memset(&allocOut, 0, sizeof(NV_ENC_CREATE_BITSTREAM_BUFFER));
|
||||
allocOut.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER;
|
||||
|
||||
NV_ENC_CREATE_INPUT_BUFFER allocSurf;
|
||||
memset(&allocSurf, 0, sizeof(NV_ENC_CREATE_INPUT_BUFFER));
|
||||
|
||||
nvenc.surf[i].format = NV_ENC_BUFFER_FORMAT_ARGB; // doesn't have RGBA!
|
||||
allocSurf.version = NV_ENC_CREATE_INPUT_BUFFER_VER;
|
||||
allocSurf.width = w;
|
||||
allocSurf.height = h;
|
||||
allocSurf.bufferFmt = nvenc.surf[i].format;
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncCreateInputBuffer(nvenc.nvenc_ctx, &allocSurf);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
nvenc.surf[i].input_surface = allocSurf.inputBuffer;
|
||||
nvenc.surf[i].width = allocSurf.width;
|
||||
nvenc.surf[i].height = allocSurf.height;
|
||||
|
||||
// output
|
||||
err = nvenc.nvenc_funcs.nvEncCreateBitstreamBuffer(nvenc.nvenc_ctx, &allocOut);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
nvenc.surf[i].output_surface = allocOut.bitstreamBuffer;
|
||||
}
|
||||
|
||||
CUcontext dummy;
|
||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nvenc_frame(const uint8_t *data, unsigned pts, uint8_t *out, uint32_t &outlen) {
|
||||
NVENCSTATUS err;
|
||||
|
||||
NV_ENC_PIC_PARAMS params;
|
||||
memset(¶ms, 0, sizeof(NV_ENC_PIC_PARAMS));
|
||||
params.version = NV_ENC_PIC_PARAMS_VER;
|
||||
params.encodePicFlags = NV_ENC_PIC_FLAG_FORCEINTRA | NV_ENC_PIC_FLAG_OUTPUT_SPSPPS;
|
||||
|
||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
||||
|
||||
NV_ENC_LOCK_INPUT_BUFFER lockBufferParams;
|
||||
memset(&lockBufferParams, 0, sizeof(NV_ENC_LOCK_INPUT_BUFFER));
|
||||
lockBufferParams.version = NV_ENC_LOCK_INPUT_BUFFER_VER;
|
||||
lockBufferParams.inputBuffer = nvenc.surf[nvenc.cursurf].input_surface;
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncLockInputBuffer(nvenc.nvenc_ctx, &lockBufferParams);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
nvenc.surf[nvenc.cursurf].pitch = lockBufferParams.pitch;
|
||||
//vlog.info("pitch %u", lockBufferParams.pitch);
|
||||
|
||||
// copy frame
|
||||
unsigned y;
|
||||
uint8_t *dst = (uint8_t *) lockBufferParams.bufferDataPtr;
|
||||
const unsigned linelen = nvenc.surf[nvenc.cursurf].width * 4;
|
||||
for (y = 0; y < (unsigned) nvenc.surf[nvenc.cursurf].height; y++) {
|
||||
memcpy(dst, data, linelen);
|
||||
data += linelen;
|
||||
dst += lockBufferParams.pitch;
|
||||
}
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncUnlockInputBuffer(nvenc.nvenc_ctx,
|
||||
nvenc.surf[nvenc.cursurf].input_surface);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
CUcontext dummy;
|
||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
||||
|
||||
params.inputBuffer = nvenc.surf[nvenc.cursurf].input_surface;
|
||||
params.bufferFmt = nvenc.surf[nvenc.cursurf].format;
|
||||
params.inputWidth = nvenc.surf[nvenc.cursurf].width;
|
||||
params.inputHeight = nvenc.surf[nvenc.cursurf].height;
|
||||
params.inputPitch = nvenc.surf[nvenc.cursurf].pitch;
|
||||
params.outputBitstream = nvenc.surf[nvenc.cursurf].output_surface;
|
||||
params.pictureStruct = NV_ENC_PIC_STRUCT_FRAME;
|
||||
params.inputTimeStamp = pts;
|
||||
|
||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncEncodePicture(nvenc.nvenc_ctx, ¶ms);
|
||||
|
||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
||||
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
|
||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
||||
|
||||
// Get output
|
||||
NV_ENC_LOCK_BITSTREAM lock_params;
|
||||
memset(&lock_params, 0, sizeof(NV_ENC_LOCK_BITSTREAM));
|
||||
|
||||
lock_params.version = NV_ENC_LOCK_BITSTREAM_VER;
|
||||
lock_params.doNotWait = 0;
|
||||
lock_params.outputBitstream = nvenc.surf[nvenc.cursurf].output_surface;
|
||||
// lock_params.sliceOffsets = slice_offsets; TODO?
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncLockBitstream(nvenc.nvenc_ctx, &lock_params);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
memcpy(out, lock_params.bitstreamBufferPtr, lock_params.bitstreamSizeInBytes);
|
||||
outlen = lock_params.bitstreamSizeInBytes;
|
||||
|
||||
err = nvenc.nvenc_funcs.nvEncUnlockBitstream(nvenc.nvenc_ctx,
|
||||
nvenc.surf[nvenc.cursurf].output_surface);
|
||||
if (err != NV_ENC_SUCCESS)
|
||||
return -1;
|
||||
|
||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
||||
|
||||
//vlog.info("Pic type %x, idr %x i %x", lock_params.pictureType, NV_ENC_PIC_TYPE_IDR,
|
||||
// NV_ENC_PIC_TYPE_I);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nvidia_unload() {
|
||||
NV_ENC_PIC_PARAMS params;
|
||||
memset(¶ms, 0, sizeof(NV_ENC_PIC_PARAMS));
|
||||
params.version = NV_ENC_PIC_PARAMS_VER;
|
||||
params.encodePicFlags = NV_ENC_PIC_FLAG_EOS;
|
||||
|
||||
nvenc.cuda_dl->cuCtxPushCurrent(nvenc.cu_ctx);
|
||||
|
||||
nvenc.nvenc_funcs.nvEncEncodePicture(nvenc.nvenc_ctx, ¶ms);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < NUM_SURF; i++) {
|
||||
nvenc.nvenc_funcs.nvEncDestroyInputBuffer(nvenc.nvenc_ctx,
|
||||
nvenc.surf[i].input_surface);
|
||||
nvenc.nvenc_funcs.nvEncDestroyBitstreamBuffer(nvenc.nvenc_ctx,
|
||||
nvenc.surf[i].output_surface);
|
||||
}
|
||||
|
||||
nvenc.nvenc_funcs.nvEncDestroyEncoder(nvenc.nvenc_ctx);
|
||||
|
||||
CUcontext dummy;
|
||||
nvenc.cuda_dl->cuCtxPopCurrent(&dummy);
|
||||
|
||||
nvenc.cuda_dl->cuCtxDestroy(nvenc.cu_ctx);
|
||||
|
||||
nvenc_free_functions(&nvenc.nvenc_dl);
|
||||
cuda_free_functions(&nvenc.cuda_dl);
|
||||
}
|
||||
/*
|
||||
int main() {
|
||||
|
||||
unsigned w = 256, h = 256, kbps = 400, fps = 15;
|
||||
|
||||
memset(&nvenc, 0, sizeof(NvencDynLoadFunctions));
|
||||
if (loadfuncs() < 0)
|
||||
return 1;
|
||||
if (setupdevice() < 0)
|
||||
return 1;
|
||||
if (setupenc(w, h, kbps, fps) < 0)
|
||||
return 1;
|
||||
if (setupsurf(w, h) < 0)
|
||||
return 1;
|
||||
|
||||
unload();
|
||||
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
int nvidia_init(const unsigned w, const unsigned h, const unsigned kbps,
|
||||
const unsigned fps) {
|
||||
|
||||
memset(&nvenc, 0, sizeof(NvencDynLoadFunctions));
|
||||
if (loadfuncs() < 0)
|
||||
return 1;
|
||||
if (setupdevice() < 0)
|
||||
return 1;
|
||||
if (setupenc(w, h, kbps, fps) < 0)
|
||||
return 1;
|
||||
if (setupsurf(w, h) < 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
#ifndef KASM_NVIDIA_H
|
||||
#define KASM_NVIDIA_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
int nvidia_init(const unsigned w, const unsigned h, const unsigned kbps,
|
||||
const unsigned fps);
|
||||
int nvenc_frame(const uint8_t *data, unsigned pts, uint8_t *out, uint32_t &outlen);
|
||||
void nvidia_unload();
|
||||
|
||||
#endif
|
@ -1 +1 @@
|
||||
Subproject commit 67466077c07377178599315b0cba01440ce6fb53
|
||||
Subproject commit cd7a460920aa2a958023222d148f29ace77ebe2c
|
Loading…
Reference in New Issue