Commit baf90c70 authored by NzSN's avatar NzSN

Submodule instead of directly copy into src.

parent 4bb7fec1
[submodule "src/protos"]
path = src/protos
url = git@gitlab.ilaihua.com:linshizhi/ffmpeg.sharedmemoryproto.git
protos @ eef0a7d6
Subproject commit eef0a7d62117d5e9f897176259551f15960b9c24
......@@ -3,6 +3,10 @@
#include <malloc.h>
#include "wasm.h"
#include <iostream>
#include <chrono>
#include <ctime>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
......@@ -100,6 +104,8 @@ EM_PORT_API(uint8_t) encodeInit(int width, int height, int fps) {
return 0;
}
static std::chrono::duration<double> total;
/* Ret Values:
* 0: Success
* 1: AGAIN
......@@ -131,13 +137,30 @@ EM_PORT_API(int) encode(uint8_t *data, uint8_t *buffer, uint32_t size, uint32_t
frame->pict_type = AV_PICTURE_TYPE_I;
++frameIndex;
auto start = std::chrono::system_clock::now();
// Encode
ret = avcodec_send_frame(cc, frame);
if (ret < 0) {
fprintf(stderr, "Fail to encoding\n");
}
return getPackets(buffer, size, osize);
ret = getPackets(buffer, size, osize);
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end - start;
total += elapsed_seconds;
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
std::cout << "finished computation at " << std::ctime(&end_time)
<< "elapsed time: " << elapsed_seconds.count() << "s"
<< std::endl
<< "total: " << total.count()
<< std::endl;
return ret;
}
/* Ret Values:
......@@ -196,8 +219,8 @@ EM_PORT_API(int) getPackets(uint8_t *buffer, uint32_t size, uint32_t *osize) {
// Muxing Parts //
///////////////////////////////////////////////////////////////////////////////
#include "ioctx.h"
#include "proto/movMemProto.h"
#include "../protos/src/ioctx.h"
#include "../protos/src/proto/movMemProto.h"
#include <vector>
#include <queue>
#include <memory>
......
#include "ioctx.h"
#include <iostream>
namespace IOCtx {
///////////////////////////////////////////////////////////////////////////////
// InOutCtx //
///////////////////////////////////////////////////////////////////////////////
AVStream* InOutCtx::getStream(StreamPredicate 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();
}
}
}
int InCtx::readFrame_(AVPacket *packet) noexcept {
return av_read_frame(fmt.get(), packet);
}
///////////////////////////////////////////////////////////////////////////////
// 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 StreamPredicate = std::function<bool(AVStream*)>;
class InOutCtx {
public:
InOutCtx() = default;
InOutCtx(std::string path, bool isCustom):
fmt(nullptr), path(path), isCustomIO(isCustom) {}
AVStream* getStream(StreamPredicate);
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*);
int readFrame_(AVPacket*) noexcept;
};
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 "movMemProto.h"
#include <algorithm>
#include <stdexcept>
namespace IOProto {
namespace MovMemProto {
int i = 0;
int MovMemProto::read_packet_internal(void *priv, uint8_t *buf, int bufSize) {
if (flag == write || bufSize < 0 || buf == nullptr) {
return AVERROR(EINVAL);
}
// No Datas
if (trans.mem.data == nullptr && s.empty()) {
return AVERROR(EAGAIN);
}
if (trans.mem.data == nullptr) {
trans.mem = s.front();
// EOF arrive
if (trans.mem.data == nullptr) {
return AVERROR_EOF;
}
trans.pos = trans.mem.data.get();
trans.remain = trans.mem.size;
s.pop();
}
size_t sizeToRead = std::min((size_t)bufSize, trans.remain);
memcpy(buf, trans.pos, sizeToRead);
// Update TransContext
trans.pos += sizeToRead;
trans.remain -= sizeToRead;
// Datas of current memory piece is all
// readed, do cleaning.
if (trans.remain == 0) {
trans.mem.data = nullptr;
trans.pos = nullptr;
trans.remain = 0;
}
return sizeToRead == 0 ? AVERROR(EAGAIN) : sizeToRead;
}
int MovMemProto::write_packet_internal(void *priv, uint8_t *buf, int bufSize) {
throw std::runtime_error("MovMemProto not support write");
}
int64_t MovMemProto::seek_packet_internal(void *opaque, int64_t offset, int whence) {
return 0;
}
void MovMemProto::close_internal() {
trans.mem.data = nullptr;
while (!s.empty()) {
auto mem = s.front();
s.pop();
}
}
}
}
#include "proto.h"
#include <memory>
#include <queue>
#ifndef MOVMEMPROTO_H
#define MOVMEMPROTO_H
namespace IOProto {
namespace MovMemProto {
struct PacketMem {
std::shared_ptr<uint8_t[]> data;
size_t size;
};
using MemPiece = PacketMem;
using Stream = std::queue<MemPiece>;
struct TransContext {
MemPiece mem;
uint8_t *pos;
size_t remain;
};
class MovMemProto: public IOProtocol<MovMemProto> {
public:
static constexpr char protoName[] = "MovMemProto";
MovMemProto(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);
/* MovMemProto temporarily only support Stream so seek function
* is ignored. */
int64_t seek_packet_internal(void *opaque, int64_t offset, int whence);
void close_internal();
void push(uint8_t data[], size_t size) {
s.push(MemPiece{ std::shared_ptr<uint8_t[]>(data), size });
}
void eof() {
s.push(MemPiece{ nullptr, 0 });
}
private:
Stream s;
TransContext trans;
};
}}
#endif /* MOVMEMPROTO_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 "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 */
......@@ -15,10 +15,12 @@ FFMPEG_ST=yes
EMSDK=/emsdk
THIRD_DIR=${WORKPATH}/lib/third/build
WASM_DIR=${WORKPATH}/src/wasm
FFMPEG_PROTO=${WORKPATH}/src/protos/src
WASM_DIR=${WORKPATH}/src/wasms
DEBUG="-O3"
#DEBUG="-O1 -g -fno-inline -gseparate-dwarf=/code/demo2/temp.debug.wasm -s SEPARATE_DWARF_URL=http://localhost:5000/temp.debug.wasm"
#DEBUG="-O1 -g -fno-inline -gseparate-dwarf=/src/demo2/temp.debug.wasm -s SEPARATE_DWARF_URL=http://localhost:5000/temp.debug.wasm"
#--closure 压缩胶水代码,有可能会造成变量重复定义。生产发布可设为1
OPTIM_FLAGS="$DEBUG --closure 0"
......@@ -41,10 +43,16 @@ FLAGS=(
-Wno-deprecated-declarations -Wno-pointer-sign -Wno-implicit-int-float-conversion -Wno-switch -Wno-parentheses -Qunused-arguments
-lavdevice -lavfilter -lavformat -lavcodec -lswresample -lswscale -lavutil -lpostproc
-lm -lharfbuzz -lfribidi -lass -lx264 -lx265 -lvpx -lwavpack -lmp3lame -lfdk-aac -lvorbis -lvorbisenc -lvorbisfile -logg -ltheora -ltheoraenc -ltheoradec -lz -lfreetype -lopus -lwebp
$WASM_DIR/interfaces.cc $WASM_DIR/utils.cc
$WASM_DIR/interfaces.cc
$FFMPEG_PROTO/utils.cc
$FFMPEG_PROTO/ioctx.cc
$FFMPEG_PROTO/proto/movMemProto.cc
$FFMPEG_PROTO/proto/proto.cc
-march=x86_32
-std=c++2a
-s FORCE_FILESYSTEM=1
-s ENVIRONMENT='web'
-s WASM=1
#-s USE_SDL=0 # use SDL2
-s INVOKE_RUN=0 # not to run the main() in the beginning
......@@ -63,5 +71,3 @@ FLAGS=(
)
emcc "${FLAGS[@]}"
mv ${DEMO_PATH}/mp4encoder.wasm tmp/mp4encoder.wasm
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