Commit de782ebc authored by Linshizhi's avatar Linshizhi

Add C++ Modules.

parent 54e035a3
import { assert } from './utils.js';
///////////////////////////////////////////////////////////////////////////////
// Message Definitions //
///////////////////////////////////////////////////////////////////////////////
export const MESSAGE_TYPE = Object.freeze({
ERROR : -1,
MSG_MIN : 0,
INIT : 0,
DESTROY : 1,
/* Once Muxer receive a 'CONNECT_PREPARE' message
* It will use information along with the message to
* change it's status to get ready to be connected by
* Encoder. */
CONNECT_PREPARE : 2,
/* Similar to 'CONNECT_PREPARE' */
DISCONNECT_PREPARE : 3,
/* Notify to oppsite that there is datas
* within shared memory */
DATA : 4,
MSG_MAX : 5,
});
export function makeMsg(type, info_) {
assert(type >= MESSAGE_TYPE.MSG_MIN &&
type <= MESSAGE_TYPE.MSG_MAX);
return { type: type, info: info_ };
}
export function makeInitMsg(shm, size) {
return makeMsg(MESSAGE_TYPE.INIT, {shm: shm, size:size})
}
export function typeOfMsg(msg) {
if ('type' in msg) {
return msg.type;
}
return MESSAGE_TYPE.ERROR;
}
export function getInfoFromMsg(msg) {
if ('info' in msg) {
return msg.info;
} else {
throw new Error("Not a encGrp message");
}
}
export function isEncGrpMsg(msg) {
return msg instanceof Object &&
'type' in msg &&
'info' in msg &&
msg.type >= MESSAGE_TYPE.MSG_MIN &&
msg.type <= MESSAGE_TYPE.MSG_MAX;
}
function isMsgWithType(msg, type) {
if (type >= MESSAGE_TYPE.MSG_MIN &&
type <= MESSAGE_TYPE.MSG_MAX) {
return false;
}
if (!(msg instanceof Object)) {
return false;
}
if ('type' in msg) {
return msg['type'] == type;
} else {
return false;
}
}
export function isInitMsg(msg) {
return isMsgWithType(msg, MESSAGE_TYPE.INIT);
}
export function isDestroyMsg(msg) {
return isMsgWithType(msg, MESSAGE_TYPE.DESTROY);
}
export function isConnectMsg(msg) {
return isMsgWithType(msg, MESSAGE_TYPE.CONNECT_PREPARE);
}
export function isDisconnectMsg(msg) {
return isMsgWithType(msg, MESSAGE_TYPE.DISCONNECT_PREPARE);
}
export function isDataMsg(msg) {
return isMsgWithType(msg, MESSAGE_TYPE.DATA);
}
///////////////////////////////////////////////////////////////////////////////
// Private Field of Channel //
///////////////////////////////////////////////////////////////////////////////
/*
* Priv fields
* ----------------------------------------------------------------------------------
* | NAN (1 Bye) | NAN (1 Byte) | NAN (1 Byte) | NAN (7 Bits) | Executing (1 Bit) |
* ----------------------------------------------------------------------------------
* */
export let PRIV_FLAGS = Object.freeze({
EXECUTING : 0x00000001,
});
#include "ioctx.h"
#include <iostream>
namespace IOCtx {
///////////////////////////////////////////////////////////////////////////////
// InOutCtx //
///////////////////////////////////////////////////////////////////////////////
AVStream* InOutCtx::getStream(StreamPrediction predict) {
AVStream *s;
AVFormatContext *fmtCtx = fmt.get();
if (fmtCtx == nullptr) {
return nullptr;
}
int nbStreams = fmtCtx->nb_streams;
for (int i = 0; i < nbStreams; ++i) {
s = fmtCtx->streams[i];
if (predict(s)) {
break;
}
}
return s;
}
bool InOutCtx::isReady() noexcept {
return fmt.get() != nullptr;
}
void InOutCtx::closeCustomIO() {
AVFormatContext *fmtCtx = fmt.get();
if (customProto.get()) {
customProto->close();
}
}
///////////////////////////////////////////////////////////////////////////////
// InCtx //
///////////////////////////////////////////////////////////////////////////////
void InCtx::readFrame(AVPacket *packet) {
int err = av_read_frame(fmt.get(), packet);
if (err < 0) {
if (err == AVERROR_EOF)
throw END_OF_FILE();
else {
throw IO_ERROR();
}
}
}
///////////////////////////////////////////////////////////////////////////////
// OutCtx //
///////////////////////////////////////////////////////////////////////////////
void OutCtx::writeFrame(AVPacket *packet) {
if (av_write_frame(static_cast<AVFormatContext*>(fmt.get()), packet) < 0) {
throw IO_ERROR();
}
}
IOCTX_ERROR OutCtx::newStream(AVCodecParameters* par) {
if (!fmt.get()) {
return ERROR;
}
AVStream *s = avformat_new_stream(fmt.get(), nullptr);
if (s == nullptr) {
return ERROR;
}
if (avcodec_parameters_copy(s->codecpar, par) < 0) {
return ERROR;
}
return OK;
}
void OutCtx::writeHeader() {
if (!isCustomIO && avio_open(
&fmt.get()->pb, path.c_str(), AVIO_FLAG_WRITE) < 0)
throw FAILED_TO_WRITE_HEADER();
AVDictionary *opts = nullptr;
if (avformat_write_header(fmt.get(), &opts) < 0) {
throw FAILED_TO_WRITE_HEADER();
}
}
void OutCtx::writeTrailer() {
av_write_trailer(fmt.get());
}
}
#include <string>
#include <stdexcept>
#include <functional>
#include "utils.h"
#include "proto/proto.h"
extern "C" {
#include <libavformat/avformat.h>
}
#ifndef IOCTX_H
#define IOCTX_H
namespace IOCtx {
// Forward Declarations
class InCtx;
class OutCtx;
class END_OF_FILE: public std::runtime_error {
public:
END_OF_FILE(): std::runtime_error("END_OF_FILE") {}
};
class IO_ERROR: public std::runtime_error {
public:
IO_ERROR(): std::runtime_error("IO_ERROR") {}
};
class FAILED_TO_WRITE_HEADER: public std::runtime_error {
public:
FAILED_TO_WRITE_HEADER(): std::runtime_error("FAILED_TO_WRITE_HEADER") {}
};
enum IOCTX_ERROR {
OK,
ERROR,
};
using StreamPrediction = std::function<bool(AVStream*)>;
class InOutCtx {
public:
InOutCtx() = default;
InOutCtx(std::string path, bool isCustom):
fmt(nullptr), path(path), isCustomIO(isCustom) {}
AVStream* getStream(StreamPrediction);
bool isReady() noexcept;
void closeCustomIO();
protected:
Utils::AVFormatContextShared fmt;
std::string path;
bool isCustomIO;
std::shared_ptr<IOProto::IOProtoInterface> customProto;
};
class InCtx: public InOutCtx {
// OutCtx require parameters of InCtx
// to initialize.
friend OutCtx;
public:
InCtx() = default;
InCtx(std::string path): InOutCtx(path, false) {
fmt = Utils::makeInAVFormat(path, nullptr);
}
template<typename T>
InCtx(std::string path, IOProto::IOProtocol<T> *proto):
InOutCtx(path, true) {
customProto = std::shared_ptr<IOProto::IOProtocol<T>>(proto);
// FIXME: AVIOContext should be generated from proto
AVIOContext *ioctx = proto->to_avioctx();
fmt = Utils::makeInAVFormat(path, ioctx);
}
void readFrame(AVPacket*);
};
class OutCtx: public InOutCtx {
public:
OutCtx() = default;
OutCtx(std::string path): InOutCtx(path, false) {
fmt = Utils::makeOutAVFormat(path, nullptr, nullptr);
}
template<typename T>
OutCtx(std::string path, IOProto::IOProtocol<T> *proto, AVOutputFormat *ofmt):
InOutCtx(path, true) {
customProto = std::shared_ptr<IOProto::IOProtocol<T>>(proto);
// FIXME: AVIOContext should be generated from proto
AVIOContext *ioctx = proto->to_avioctx();
ioctx->direct = false;
fmt = Utils::makeOutAVFormat(path, ioctx, ofmt);
}
void writeFrame(AVPacket*);
IOCTX_ERROR newStream(AVCodecParameters*);
void writeHeader();
void writeTrailer();
};
}
#endif /* IOCTX_H */
#include <string>
#include <memory>
#include <functional>
extern "C" {
#include <libavformat/avformat.h>
}
#ifndef PROTO_H
#define PROTO_H
namespace IOProto {
constexpr size_t DEFAULT_BUFFER_SIZE = 32768;
enum RW_FLAG {
read,
write
};
class IOProtoInterface {
public:
virtual ~IOProtoInterface() {};
virtual void close() = 0;
};
/* IOProtocol used to wrap IO Protocol logics
* and transform into AVIOContext */
template<typename T>
class IOProtocol: public IOProtoInterface {
public:
IOProtocol(std::string name, RW_FLAG flag, void *priv):
name(name), flag(flag), io(nullptr), priv(priv) {}
~IOProtocol() {};
int read_packet(void *priv, uint8_t *buf, int bufSize) {
return static_cast<T*>(this)->read_packet_internal(priv, buf, bufSize);
}
int write_packet(void *priv, uint8_t *buf, int bufSize) {
return static_cast<T*>(this)->write_packet_internal(priv, buf, bufSize);
}
int64_t seek_packet(void *opaque, int64_t offset, int whence) {
return static_cast<T*>(this)->seek_packet_internal(opaque, offset, whence);
}
void close() {
static_cast<T*>(this)->close_internal();
}
AVIOContext* to_avioctx() noexcept {
if (io == nullptr) {
// This buffer is managed by libav
uint8_t *buffer = new uint8_t[DEFAULT_BUFFER_SIZE];
io = avio_alloc_context(
buffer, DEFAULT_BUFFER_SIZE, flag == write, static_cast<void*>(this),
[](void *priv, uint8_t *buf, int bufSize) -> int {
return static_cast<T*>(priv)->read_packet(priv, buf, bufSize);
},
[](void *priv, uint8_t *buf, int bufSize) -> int {
return static_cast<T*>(priv)->write_packet(priv, buf, bufSize);
},
[](void *priv, int64_t offset, int whence) -> int64_t {
return static_cast<T*>(priv)->seek_packet(priv, offset, whence);
});
}
return io;
}
protected:
RW_FLAG flag;
void *priv;
std::string name;
AVIOContext *io;
};
}
#endif /* PROTO_H */
#include "proto.h"
#ifndef PROTOLISTS_H
#define PROTOLISTS_H
namespace IOProto {
}
#endif /* PROTOLISTS_H */
#include "proto.h"
#ifndef SHAREDMEMPROTO_H
#define SHAREDMEMPROTO_H
namespace IOProto {
class SharedMemProto: public IOProtocol<SharedMemProto> {
public:
SharedMemProto(void *priv, RW_FLAG flag):
IOProtocol("", flag, priv) {}
};
}
#endif /* SHAREDMEMPROTO_H */
#include "transientMemProto.h"
#include <memory>
#include <algorithm>
#include <queue>
extern "C" {
#include <malloc.h>
}
using std::queue;
namespace IOProto {
namespace TransientMemProto {
/* External buffer must not be realeased
* by TransientMemProto */
struct BufferInfo {
uint8_t *buffer;
uint8_t *pos;
size_t remain;
};
bool eof = false;
BufferInfo current;
queue<BufferInfo> externalBuffers;
// Configurations
static bool isRleaseExtBuf = false;
static bool debugging = true;
constexpr size_t buffMaxSize = 33554432;
uint64_t dataSize = 0;
uint8_t buff[buffMaxSize];
uint8_t *buffCurrent = buff;
uint8_t *buffEnd = buff + buffMaxSize;
int numOfFrames() {
return externalBuffers.size();
}
void config_releaseExtBuf(bool onoff) {
isRleaseExtBuf = onoff;
}
/* Note: The passed buf should be full filled */
void attachBuffer(uint8_t *buf, size_t bufSize) {
externalBuffers.push({ buf, buf, bufSize });
}
int TransientMemProto::read_packet_internal(void *priv, uint8_t *buf, int bufSize) {
size_t readSize = 0;
if(flag == write || bufSize < 0 || buf == nullptr) {
AVERROR(EINVAL);
}
// No Datas
if (current.remain == 0 && externalBuffers.empty()) {
if (eof)
return AVERROR_EOF;
else
return AVERROR(EAGAIN);
}
if (current.remain == 0) {
current = externalBuffers.front();
externalBuffers.pop();
}
readSize = std::min((size_t)bufSize, current.remain);
// Read data
memcpy(buf, current.pos, readSize);
// Update Buffer status
current.pos += readSize;
current.remain -= readSize;
if (current.remain == 0 && isRleaseExtBuf) {
delete current.buffer;
}
return readSize == 0 ? AVERROR(EAGAIN) : readSize;
}
int TransientMemProto::write_packet_internal(void *priv, uint8_t *buf, int bufSize) {
uint8_t *newBuf = new uint8_t[bufSize];
memcpy(newBuf, buf, bufSize);
attachBuffer(newBuf, bufSize);
return bufSize;
}
int64_t TransientMemProto::seek_packet_internal(void *opaque, int64_t offset, int whence) {
return 0;
}
void TransientMemProto::close_internal() {
eof = true;
}
}
}
#include "proto.h"
#ifndef TRANSIENTMEMPROTO_H
#define TRANSIENTMEMPROTO_H
namespace IOProto {
namespace TransientMemProto {
extern uint64_t dataSize;
void config_releaseExtBuf(bool onoff);
bool isEmpty();
int numOfFrames();
/* Note: The passed buf should be full filled */
void attachBuffer(uint8_t *buf, size_t bufSize);
class TransientMemProto: public IOProtocol<TransientMemProto> {
public:
static constexpr char protoName[] = "TransientMemProto";
TransientMemProto(void *priv, RW_FLAG flag):
IOProtocol(protoName, flag, priv) {}
int read_packet_internal(void *priv, uint8_t *buf, int bufSize);
int write_packet_internal(void *priv, uint8_t *buf, int bufSize);
int64_t seek_packet_internal(void *opaque, int64_t offset, int whence);
void close_internal();
};
}
}
#endif /* TRANSIENTMEMPROTO_H */
#include "utils.h"
#include <iostream>
namespace Utils {
static AVFormatContext* AVFormatInputContextConstructor(
std::string path, AVIOContext *customIO) {
AVFormatContext *ctx = nullptr;
if (customIO != nullptr) {
ctx = avformat_alloc_context();
ctx->pb = customIO;
}
if (avformat_open_input(&ctx, path.c_str(), nullptr, nullptr) < 0) {
return nullptr;
}
if (avformat_find_stream_info(ctx, nullptr) < 0) {
avformat_close_input(&ctx);
return nullptr;
}
return ctx;
}
static void AVFormatInputContextDestructor(AVFormatContext *ctx) {
avformat_close_input(&ctx);
}
AVFormatContextShared makeInAVFormat(std::string path, AVIOContext *customIO) {
AVFormatContextShared ioCtx {
AVFormatInputContextConstructor(path, customIO),
AVFormatInputContextDestructor
};
return ioCtx;
}
static AVFormatContext* AVFormatOutputContextConstructor(
std::string path, AVIOContext *customIO, AVOutputFormat *customFormat) {
AVFormatContext *ctx = nullptr;
if (avformat_alloc_output_context2(&ctx, nullptr, nullptr, path.c_str()) < 0) {
return nullptr;
}
if (customIO != nullptr) {
ctx->pb = customIO;
ctx->flags = ctx->flags | AVFMT_FLAG_CUSTOM_IO;
}
return ctx;
}
static void AVFormatOutputContextDestructor(AVFormatContext *ctx) {
avformat_close_input(&ctx);
}
AVFormatContextShared makeOutAVFormat(
std::string path, AVIOContext *customIO, AVOutputFormat *customFormat) {
AVFormatContextShared ioCtx {
AVFormatOutputContextConstructor(path, customIO, customFormat),
AVFormatOutputContextDestructor
};
return ioCtx;
}
bool isVideoValid(std::string path) {
if (path.empty()) {
return false;
}
std::string cmd =
"ffmpeg -v error -i " + path + " -f null - 2>./log.txt";
int ret = system(cmd.c_str());
return ret == 0 ? true : false;
}
void packetPrepareForOutput(AVPacket *packet, AVStream *inStream, AVStream *outStream) {
packet->pts = av_rescale_q_rnd(packet->pts, inStream->time_base, outStream->time_base,
static_cast<AVRounding>(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
packet->dts = av_rescale_q_rnd(packet->dts, inStream->time_base, outStream->time_base,
static_cast<AVRounding>(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
packet->duration = av_rescale_q(packet->duration, inStream->time_base, outStream->time_base);
packet->stream_index = 0;
packet->pos = -1;
}
}
#include <string>
#include <memory>
extern "C" {
#include <libavformat/avformat.h>
}
#ifndef UTILS_H
#define UTILS_H
namespace Utils {
using AVFormatContextShared = std::shared_ptr<AVFormatContext>;
AVFormatContextShared makeInAVFormat(std::string, AVIOContext*);
AVFormatContextShared makeOutAVFormat(std::string, AVIOContext*, AVOutputFormat*);
/* To check that is video specified by path valid
*
* This function require that ffmpeg is installed on
* running envrionment */
bool isVideoValid(std::string path);
void packetPrepareForOutput(AVPacket *packet, AVStream *inStream, AVStream *outStream);
}
#endif /* UTILS_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment