Port the old x264 code
parent
9144045718
commit
c961c56c39
@ -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;
|
||||||
|
}
|
Loading…
Reference in New Issue