From c961c56c392f375b02950dda2f1138fcbda06776 Mon Sep 17 00:00:00 2001 From: Lauri Kasanen Date: Mon, 14 Jun 2021 13:31:20 +0300 Subject: [PATCH] Port the old x264 code --- common/rfb/CMakeLists.txt | 5 + common/rfb/EncodeManager.cxx | 57 +++-- common/rfb/ServerCore.cxx | 4 + common/rfb/ServerCore.h | 1 + common/rfb/TightConstants.h | 3 +- common/rfb/TightX264Encoder.cxx | 318 ++++++++++++++++++++++++ common/rfb/TightX264Encoder.h | 72 ++++++ common/rfb/mp4.c | 98 ++++++++ common/rfb/mp4.h | 197 +++++++++++++++ common/rfb/mp4_bitbuf.c | 193 +++++++++++++++ common/rfb/mp4_moof.c | 194 +++++++++++++++ common/rfb/mp4_moov.c | 420 ++++++++++++++++++++++++++++++++ unix/xserver/hw/vnc/Makefile.am | 2 +- unix/xserver/hw/vnc/Xvnc.man | 4 + 14 files changed, 1550 insertions(+), 18 deletions(-) create mode 100644 common/rfb/TightX264Encoder.cxx create mode 100644 common/rfb/TightX264Encoder.h create mode 100644 common/rfb/mp4.c create mode 100644 common/rfb/mp4.h create mode 100644 common/rfb/mp4_bitbuf.c create mode 100644 common/rfb/mp4_moof.c create mode 100644 common/rfb/mp4_moov.c diff --git a/common/rfb/CMakeLists.txt b/common/rfb/CMakeLists.txt index 38d3472..3e9bcb7 100644 --- a/common/rfb/CMakeLists.txt +++ b/common/rfb/CMakeLists.txt @@ -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) diff --git a/common/rfb/EncodeManager.cxx b/common/rfb/EncodeManager.cxx index fb9b8a6..2d49824 100644 --- a/common/rfb/EncodeManager.cxx +++ b/common/rfb/EncodeManager.cxx @@ -40,6 +40,7 @@ #include #include #include +#include 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,20 +398,32 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_, conn->writer()->writeFramebufferUpdateStart(nRects); - writeCopyRects(copied, copyDelta); - writeCopyPassRects(copypassed); - /* - * We start by searching for solid rects, which are then removed - * from the changed region. + * In extra-low-quality mode, if x264 is enabled, send entire screen frames */ - if (conn->cp.supportsLastRect) - writeSolidRects(&changed, pb); + if (rfb::Server::x264Bitrate && videoDetected) { + std::vector rects; + changed.get_rects(&rects); + updateVideoStats(rects, pb); - writeRects(changed, pb, - &start, true); - if (!videoDetected) // In case detection happened between the calls - writeRects(cursorRegion, renderedCursor); + writeSubRect(pb->getRect(), pb, encoderFullColour, Palette(), std::vector(), + false); + } else { + writeCopyRects(copied, copyDelta); + writeCopyPassRects(copypassed); + + /* + * We start by searching for solid rects, which are then removed + * from the changed region. + */ + if (conn->cp.supportsLastRect) + writeSolidRects(&changed, pb); + + writeRects(changed, pb, + &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 &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 &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; } diff --git a/common/rfb/ServerCore.cxx b/common/rfb/ServerCore.cxx index 3b36af0..f48eeaa 100644 --- a/common/rfb/ServerCore.cxx +++ b/common/rfb/ServerCore.cxx @@ -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.", diff --git a/common/rfb/ServerCore.h b/common/rfb/ServerCore.h index 280596f..8e0d348 100644 --- a/common/rfb/ServerCore.h +++ b/common/rfb/ServerCore.h @@ -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; diff --git a/common/rfb/TightConstants.h b/common/rfb/TightConstants.h index 5ea1939..2d28d95 100644 --- a/common/rfb/TightConstants.h +++ b/common/rfb/TightConstants.h @@ -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; diff --git a/common/rfb/TightX264Encoder.cxx b/common/rfb/TightX264Encoder.cxx new file mode 100644 index 0000000..e2f7367 --- /dev/null +++ b/common/rfb/TightX264Encoder.cxx @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#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); + } + } +} diff --git a/common/rfb/TightX264Encoder.h b/common/rfb/TightX264Encoder.h new file mode 100644 index 0000000..8ed43a6 --- /dev/null +++ b/common/rfb/TightX264Encoder.h @@ -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 +#include +#include + +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 diff --git a/common/rfb/mp4.c b/common/rfb/mp4.c new file mode 100644 index 0000000..46a0d37 --- /dev/null +++ b/common/rfb/mp4.c @@ -0,0 +1,98 @@ +#include "mp4.h" + +#include + +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; +} diff --git a/common/rfb/mp4.h b/common/rfb/mp4.h new file mode 100644 index 0000000..e7adf22 --- /dev/null +++ b/common/rfb/mp4.h @@ -0,0 +1,197 @@ +#pragma once +#include +#include +#include +#include + +#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 diff --git a/common/rfb/mp4_bitbuf.c b/common/rfb/mp4_bitbuf.c new file mode 100644 index 0000000..e97d99f --- /dev/null +++ b/common/rfb/mp4_bitbuf.c @@ -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; +// } diff --git a/common/rfb/mp4_moof.c b/common/rfb/mp4_moof.c new file mode 100644 index 0000000..f32078a --- /dev/null +++ b/common/rfb/mp4_moof.c @@ -0,0 +1,194 @@ +#include "mp4.h" +#include + +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; +} diff --git a/common/rfb/mp4_moov.c b/common/rfb/mp4_moov.c new file mode 100644 index 0000000..1a90576 --- /dev/null +++ b/common/rfb/mp4_moov.c @@ -0,0 +1,420 @@ +#include "mp4.h" + +#include + +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 + 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 // 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 // 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 // Component name + err = put_u32_be_to_offset(ptr, start_atom, ptr->offset - start_atom); chk_err + return BUF_OK; +} diff --git a/unix/xserver/hw/vnc/Makefile.am b/unix/xserver/hw/vnc/Makefile.am index 3692555..e3a5ced 100644 --- a/unix/xserver/hw/vnc/Makefile.am +++ b/unix/xserver/hw/vnc/Makefile.am @@ -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 diff --git a/unix/xserver/hw/vnc/Xvnc.man b/unix/xserver/hw/vnc/Xvnc.man index 809fc1c..2976888 100644 --- a/unix/xserver/hw/vnc/Xvnc.man +++ b/unix/xserver/hw/vnc/Xvnc.man @@ -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