Port the old x264 code

video
Lauri Kasanen 4 years ago
parent 9144045718
commit c961c56c39

@ -58,6 +58,7 @@ set(RFB_SOURCES
TightEncoder.cxx
TightJPEGEncoder.cxx
TightWEBPEncoder.cxx
TightX264Encoder.cxx
UpdateTracker.cxx
VNCSConnectionST.cxx
VNCServerST.cxx
@ -65,6 +66,10 @@ set(RFB_SOURCES
ZRLEDecoder.cxx
encodings.cxx
util.cxx
mp4.c
mp4_bitbuf.c
mp4_moov.c
mp4_moof.c
xxhash.c)
if(UNIX)

@ -40,6 +40,7 @@
#include <rfb/TightEncoder.h>
#include <rfb/TightJPEGEncoder.h>
#include <rfb/TightWEBPEncoder.h>
#include <rfb/TightX264Encoder.h>
using namespace rfb;
@ -68,6 +69,7 @@ enum EncoderClass {
encoderTight,
encoderTightJPEG,
encoderTightWEBP,
encoderTightX264,
encoderZRLE,
encoderClassMax,
};
@ -110,6 +112,8 @@ static const char *encoderClassName(EncoderClass klass)
return "Tight (JPEG)";
case encoderTightWEBP:
return "Tight (WEBP)";
case encoderTightX264:
return "Tight (X264)";
case encoderZRLE:
return "ZRLE";
case encoderClassMax:
@ -170,6 +174,7 @@ EncodeManager::EncodeManager(SConnection* conn_, EncCache *encCache_) : conn(con
encoders[encoderTight] = new TightEncoder(conn);
encoders[encoderTightJPEG] = new TightJPEGEncoder(conn);
encoders[encoderTightWEBP] = new TightWEBPEncoder(conn);
encoders[encoderTightX264] = new TightX264Encoder(conn, encCache, encoderTightX264);
encoders[encoderZRLE] = new ZRLEEncoder(conn);
webpBenchResult = ((TightWEBPEncoder *) encoders[encoderTightWEBP])->benchmark();
@ -393,6 +398,17 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
conn->writer()->writeFramebufferUpdateStart(nRects);
/*
* In extra-low-quality mode, if x264 is enabled, send entire screen frames
*/
if (rfb::Server::x264Bitrate && videoDetected) {
std::vector<Rect> rects;
changed.get_rects(&rects);
updateVideoStats(rects, pb);
writeSubRect(pb->getRect(), pb, encoderFullColour, Palette(), std::vector<uint8_t>(),
false);
} else {
writeCopyRects(copied, copyDelta);
writeCopyPassRects(copypassed);
@ -407,6 +423,7 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
&start, true);
if (!videoDetected) // In case detection happened between the calls
writeRects(cursorRegion, renderedCursor);
}
updateQualities();
@ -617,6 +634,8 @@ Encoder *EncodeManager::startRect(const Rect& rect, int type, const bool trackQu
klass = activeEncoders[activeType];
if (isWebp)
klass = encoderTightWEBP;
else if (rfb::Server::x264Bitrate && videoDetected) // if x264 enabled
klass = encoderTightX264;
beforeLength = conn->getOutStream()->length();
@ -656,6 +675,8 @@ void EncodeManager::endRect(const uint8_t isWebp)
klass = activeEncoders[activeType];
if (isWebp)
klass = encoderTightWEBP;
else if (rfb::Server::x264Bitrate && videoDetected) // if x264 enabled
klass = encoderTightX264;
stats[klass][activeType].bytes += length;
}
@ -861,6 +882,8 @@ void EncodeManager::updateVideoStats(const std::vector<Rect> &rects, const Pixel
uint32_t i;
if (!rfb::Server::videoTime) {
if (!videoDetected)
((TightX264Encoder *) encoders[encoderTightX264])->setKeyframe();
videoDetected = true;
return;
}
@ -888,6 +911,8 @@ void EncodeManager::updateVideoStats(const std::vector<Rect> &rects, const Pixel
if (area > (unsigned) rfb::Server::videoArea) {
// Initiate low-quality video mode
if (!videoDetected)
((TightX264Encoder *) encoders[encoderTightX264])->setKeyframe();
videoDetected = true;
videoTimer.start(1000 * rfb::Server::videoOutTime);
}
@ -1022,11 +1047,6 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
webpTookTooLong = false;
changed.get_rects(&rects);
// Update stats
if (mainScreen) {
updateVideoStats(rects, pb);
}
if (videoDetected) {
rects.clear();
rects.push_back(pb->getRect());
@ -1171,6 +1191,11 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
writeSubRect(subrects[i], pb, encoderTypes[i], palettes[i], compresseds[i], isWebp[i]);
}
// Update stats
if (mainScreen) {
updateVideoStats(rects, pb);
}
if (scaledpb)
delete scaledpb;
}

@ -199,6 +199,10 @@ rfb::IntParameter rfb::Server::videoScaling
("VideoScaling",
"Scaling method to use when in downscaled video mode. 0 = nearest, 1 = bilinear, 2 = prog bilinear",
2, 0, 2);
rfb::IntParameter rfb::Server::x264Bitrate
("x264Bitrate",
"Enable x264 encoding for full-screen video, in kbps. Default 0 (off)",
0, 0, 2000);
rfb::BoolParameter rfb::Server::printVideoArea
("PrintVideoArea",
"Print the detected video area % value.",

@ -59,6 +59,7 @@ namespace rfb {
static IntParameter videoOutTime;
static IntParameter videoArea;
static IntParameter videoScaling;
static IntParameter x264Bitrate;
static StringParameter kasmPasswordFile;
static BoolParameter printVideoArea;
static BoolParameter protocol3_3;

@ -24,7 +24,8 @@ namespace rfb {
const unsigned int tightJpeg = 0x09;
const unsigned int tightPng = 0x0a;
const unsigned int tightWebp = 0x0b;
const unsigned int tightMaxSubencoding = 0x0b;
const unsigned int tightX264 = 0x0c;
const unsigned int tightMaxSubencoding = 0x0c;
// Filters to improve compression efficiency
const unsigned int tightFilterCopy = 0x00;

@ -0,0 +1,318 @@
/* 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 "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);
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),
encCache(cache_), cacheType(cacheType_),
framebuf(NULL), framelen(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];
mux = new Mp4Context;
memset(mux, 0, sizeof(Mp4Context));
muxstate = new Mp4State;
memset(muxstate, 0, sizeof(Mp4State));
}
TightX264Encoder::~TightX264Encoder()
{
delete params;
delete [] framebuf;
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
os = conn->getOutStream();
uint32_t w, h;
w = pb->width();
h = pb->height();
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);
}
}
}

@ -0,0 +1,72 @@
/* 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; }
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;
EncCache *encCache;
uint8_t cacheType;
public:
uint8_t *framebuf;
uint32_t framelen;
};
}
#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;
}

@ -46,7 +46,7 @@ Xvnc_CPPFLAGS = $(XVNC_CPPFLAGS) -DKASMVNC -DNO_MODULE_EXTS \
-I$(top_srcdir)/include ${XSERVERLIBS_CFLAGS} -I$(includedir)
Xvnc_LDADD = $(XVNC_LIBS) libvnccommon.la $(COMMON_LIBS) \
$(XSERVER_LIBS) $(XSERVER_SYS_LIBS) $(XVNC_SYS_LIBS) -lX11 -lwebp -lssl -lcrypto -lcrypt
$(XSERVER_LIBS) $(XSERVER_SYS_LIBS) $(XVNC_SYS_LIBS) -lX11 -lwebp -lssl -lcrypto -lcrypt -lx264
Xvnc_LDFLAGS = $(LD_EXPORT_SYMBOLS_FLAG) -fopenmp

@ -256,6 +256,10 @@ Scaling method to use when in downscaled video mode. 0 = nearest, 1 = bilinear,
Default \fB2\fP.
.
.TP
.B \-x264Bitrate \fInum\fP
Enable x264 encoding for full-screen video, in kbps. Default \fB0\fP (off).
.
.TP
.B \-CompareFB \fImode\fP
Perform pixel comparison on framebuffer to reduce unnecessary updates. Can
be either \fB0\fP (off), \fB1\fP (always) or \fB2\fP (auto). Default is

Loading…
Cancel
Save