Switch from WebM to full Matroska muxer
This commit is contained in:
parent
cc88f1f0dd
commit
50c9a7a6ff
5
Makefile
5
Makefile
@ -16,7 +16,7 @@ all:
|
|||||||
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o img.o hi/img.c $(LDFLAGS)
|
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o img.o hi/img.c $(LDFLAGS)
|
||||||
$(CXX) $(CXXFLAGS) -std=c++11 -shared -c -o webmdec.o hi/webmdec.cpp $(LDFLAGS)
|
$(CXX) $(CXXFLAGS) -std=c++11 -shared -c -o webmdec.o hi/webmdec.cpp $(LDFLAGS)
|
||||||
$(CXX) $(CXXFLAGS) -std=c++11 -shared -c -o webmenc.o hi/webmenc.cpp $(LDFLAGS)
|
$(CXX) $(CXXFLAGS) -std=c++11 -shared -c -o webmenc.o hi/webmenc.cpp $(LDFLAGS)
|
||||||
$(CC) $(CXXFLAGS) -std=c++11 -shared -c -o vpxenc.o hi/vpxenc.c $(LDFLAGS)
|
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o vpxenc.o hi/vpxenc.c $(LDFLAGS)
|
||||||
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o opus.o hi/opus.c $(LDFLAGS)
|
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o opus.o hi/opus.c $(LDFLAGS)
|
||||||
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o webcam.o hi/webcam.c $(LDFLAGS)
|
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o webcam.o hi/webcam.c $(LDFLAGS)
|
||||||
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o scale.o hi/relay.c $(LDFLAGS)
|
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o scale.o hi/relay.c $(LDFLAGS)
|
||||||
@ -24,6 +24,7 @@ all:
|
|||||||
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o h264enc.o hi/h264enc.c $(LDFLAGS)
|
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o h264enc.o hi/h264enc.c $(LDFLAGS)
|
||||||
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o rtmp.o hi/rtmp.c $(LDFLAGS)
|
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o rtmp.o hi/rtmp.c $(LDFLAGS)
|
||||||
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o aaclc.o hi/aaclc.c $(LDFLAGS)
|
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o aaclc.o hi/aaclc.c $(LDFLAGS)
|
||||||
$(CC) $(CXXFLAGS) -shared -o libcutihi.so -shared node.o webmdec.o webmenc.o window.o microphone.o mode.o img.o opus.o webcam.o scale.o minitrace.o h264enc.o rtmp.o aaclc.o vpxenc.o $(LDFLAGS)
|
$(CC) $(CXXFLAGS) -std=c99 -shared -c -o mkv.o hi/mkv.c $(LDFLAGS)
|
||||||
|
$(CC) $(CXXFLAGS) -shared -o libcutihi.so -shared node.o webmdec.o webmenc.o window.o microphone.o mode.o img.o opus.o webcam.o scale.o minitrace.o h264enc.o rtmp.o aaclc.o vpxenc.o mkv.o $(LDFLAGS)
|
||||||
|
|
||||||
$(CXX) $(CXXFLAGS) -std=c++11 `wx-config --cflags base,adv,core,aui` -o cuticle ui/main.cpp ui/frame.cpp ui/textctrl.cpp ui/timeline.cpp -L./ -lcutihi $(LDFLAGS) `wx-config --libs base,adv,core,aui`
|
$(CXX) $(CXXFLAGS) -std=c++11 `wx-config --cflags base,adv,core,aui` -o cuticle ui/main.cpp ui/frame.cpp ui/textctrl.cpp ui/timeline.cpp -L./ -lcutihi $(LDFLAGS) `wx-config --libs base,adv,core,aui`
|
||||||
|
74
hi/bs.h
74
hi/bs.h
@ -5,6 +5,9 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include<stdlib.h>
|
||||||
|
#include<string.h>
|
||||||
|
|
||||||
#define CUTIHI_BS_FLAG_KEY 1
|
#define CUTIHI_BS_FLAG_KEY 1
|
||||||
#define CUTIHI_BS_SETUP_PACKET 2
|
#define CUTIHI_BS_SETUP_PACKET 2
|
||||||
|
|
||||||
@ -20,6 +23,77 @@ typedef struct {
|
|||||||
CHiBSFrame data[];
|
CHiBSFrame data[];
|
||||||
} CHiBSFrames;
|
} CHiBSFrames;
|
||||||
|
|
||||||
|
static inline CHiBSFrames *CHi_BS_Combine(CHiBSFrames *A, const CHiBSFrames *B) {
|
||||||
|
CHiBSFrames *ret = NULL;
|
||||||
|
|
||||||
|
size_t aoffset = 0;
|
||||||
|
|
||||||
|
if(A) {
|
||||||
|
aoffset = A->count;
|
||||||
|
|
||||||
|
ret = (CHiBSFrames*) calloc(1, sizeof(*ret) + sizeof(CHiBSFrame) * (A->count + B->count));
|
||||||
|
ret->count = A->count + B->count;
|
||||||
|
memcpy(ret->data, A->data, sizeof(*A->data) * A->count);
|
||||||
|
|
||||||
|
free(A);
|
||||||
|
} else {
|
||||||
|
ret = (CHiBSFrames*) calloc(1, sizeof(*ret) + sizeof(CHiBSFrame) * B->count);
|
||||||
|
ret->count = B->count;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < B->count; i++) {
|
||||||
|
void *copy = malloc(B->data[i].sz);
|
||||||
|
memcpy(copy, B->data[i].ptr, B->data[i].sz);
|
||||||
|
|
||||||
|
ret->data[aoffset + i] = (CHiBSFrame) {
|
||||||
|
.timestamp = B->data[i].timestamp,
|
||||||
|
.sz = B->data[i].sz,
|
||||||
|
.flags = B->data[i].flags,
|
||||||
|
.ptr = copy
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline CHiBSFrames *CHi_BS_Empty() {
|
||||||
|
return (CHiBSFrames*) calloc(1, sizeof(CHiBSFrames));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CHi_BS_Pop(CHiBSFrames *bsfs, size_t num) {
|
||||||
|
for(size_t i = 0; i < num; i++) {
|
||||||
|
free(bsfs->data[i].ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
memmove(&bsfs->data[0], &bsfs->data[num], sizeof(bsfs->data[0]) * (bsfs->count - num));
|
||||||
|
|
||||||
|
bsfs->count -= num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CHi_BS_Clear(CHiBSFrames *bsfs) {
|
||||||
|
CHi_BS_Pop(bsfs, bsfs->count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void CHi_BS_Free(CHiBSFrames *bsfs) {
|
||||||
|
if(bsfs) {
|
||||||
|
CHi_BS_Clear(bsfs);
|
||||||
|
free(bsfs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline CHiBSFrames *CHi_BS_Grow(CHiBSFrames *bsfs, size_t num) {
|
||||||
|
size_t oldsz = sizeof(*bsfs) + sizeof(CHiBSFrame) * bsfs->count;
|
||||||
|
size_t newsz = sizeof(*bsfs) + sizeof(CHiBSFrame) * (bsfs->count + num);
|
||||||
|
CHiBSFrames *ret = (CHiBSFrames*) realloc(bsfs, newsz);
|
||||||
|
memset((uint8_t*) ret + oldsz, 0, newsz - oldsz);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t width;
|
||||||
|
uint16_t height;
|
||||||
|
} CHiVPxBSSettings;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
611
hi/mkv.c
Normal file
611
hi/mkv.c
Normal file
@ -0,0 +1,611 @@
|
|||||||
|
#include"node.h"
|
||||||
|
|
||||||
|
#include<vpx/vpx_encoder.h>
|
||||||
|
#include<vpx/vp8cx.h>
|
||||||
|
|
||||||
|
#include<eebie/writer.h>
|
||||||
|
|
||||||
|
#include<assert.h>
|
||||||
|
|
||||||
|
#include"mode.h"
|
||||||
|
|
||||||
|
#include"img.h"
|
||||||
|
#include<math.h>
|
||||||
|
|
||||||
|
#include<smmintrin.h>
|
||||||
|
|
||||||
|
#include<string.h>
|
||||||
|
|
||||||
|
#include"minitrace.h"
|
||||||
|
|
||||||
|
#include"linearity.h"
|
||||||
|
|
||||||
|
#include"yuv.h"
|
||||||
|
|
||||||
|
#include<stdio.h>
|
||||||
|
|
||||||
|
#include<arpa/inet.h>
|
||||||
|
#include<sys/random.h>
|
||||||
|
|
||||||
|
#define NALLENSZ 4
|
||||||
|
|
||||||
|
static size_t annexb_parse(const uint8_t *src, const uint8_t *srcEnd) {
|
||||||
|
int zeros = 0;
|
||||||
|
const uint8_t *src2;
|
||||||
|
for(src2 = src; src2 != srcEnd; src2++) {
|
||||||
|
if(*src2 == 0) {
|
||||||
|
zeros++;
|
||||||
|
} else if((zeros == 2 || zeros == 3) && *src2 == 1) {
|
||||||
|
src2 -= zeros;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
zeros = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return src2 - src;
|
||||||
|
}
|
||||||
|
|
||||||
|
// I want to die.
|
||||||
|
static void get_dimensions_from_extradata(const uint8_t *src, uint16_t *foundWidth, uint16_t *foundHeight) {
|
||||||
|
src += 8;
|
||||||
|
|
||||||
|
int nal_unit = *(src++);
|
||||||
|
int profile_idc = *(src++);
|
||||||
|
int constraints = *(src++);
|
||||||
|
int level_idc = *(src++);
|
||||||
|
|
||||||
|
int constraint_set0_flag = (constraints >> 7) & 1;
|
||||||
|
int constraint_set1_flag = (constraints >> 6) & 1;
|
||||||
|
int constraint_set2_flag = (constraints >> 5) & 1;
|
||||||
|
int constraint_set3_flag = (constraints >> 4) & 1;
|
||||||
|
int constraint_set4_flag = (constraints >> 3) & 1;
|
||||||
|
int constraint_set5_flag = (constraints >> 2) & 1;
|
||||||
|
|
||||||
|
size_t bitoffset = 0;
|
||||||
|
|
||||||
|
#define ZOOOZOOO_BIT(b) do { b = ((src[0] >> (bitoffset = (bitoffset + 7) % 8)) & 1); if(bitoffset == 0) src++; } while(0);
|
||||||
|
#define ZOOOZOOO_READ(out, n) do { out = 0; int b; for(int i_i = 0; i_i < n; i_i++) { ZOOOZOOO_BIT(b); out |= (b << (n - i_i - 1)); } } while(0);
|
||||||
|
#define ZOOOZOOO_UE(out) do { int i = 0; int b = 0; while(1) { ZOOOZOOO_BIT(b); if(b == 1 || i >= 32) break; i++; } ZOOOZOOO_READ(out, i); out += (1 << i) - 1; } while(0);
|
||||||
|
#define ZOOOZOOO_SE(out) do { ZOOOZOOO_UE(out); if(out & 1) { out = (out + 1) / 2; } else { out = -(out / 2); } } while(0);
|
||||||
|
|
||||||
|
int seq_parameter_set_id;
|
||||||
|
ZOOOZOOO_UE(seq_parameter_set_id);
|
||||||
|
|
||||||
|
if(profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || profile_idc == 86 || profile_idc == 118) {
|
||||||
|
int chroma_format_idc;
|
||||||
|
ZOOOZOOO_UE(chroma_format_idc);
|
||||||
|
|
||||||
|
if(chroma_format_idc == 3) {
|
||||||
|
int residual_color_transform_flag;
|
||||||
|
ZOOOZOOO_BIT(residual_color_transform_flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bit_depth_luma_minus8;
|
||||||
|
ZOOOZOOO_UE(bit_depth_luma_minus8);
|
||||||
|
|
||||||
|
int bit_depth_chroma_minus8;
|
||||||
|
ZOOOZOOO_UE(bit_depth_chroma_minus8);
|
||||||
|
|
||||||
|
int qpprime_y_zero_transform_bypass_flag;
|
||||||
|
ZOOOZOOO_BIT(qpprime_y_zero_transform_bypass_flag);
|
||||||
|
|
||||||
|
int seq_scaling_matrix_present_flag;
|
||||||
|
ZOOOZOOO_BIT(seq_scaling_matrix_present_flag);
|
||||||
|
|
||||||
|
if(seq_scaling_matrix_present_flag) {
|
||||||
|
int i = 0;
|
||||||
|
int lim = chroma_format_idc != 3 ? 8 : 12;
|
||||||
|
for(i = 0; i < lim; i++) {
|
||||||
|
int seq_scaling_list_present_flag;
|
||||||
|
ZOOOZOOO_BIT(seq_scaling_list_present_flag);
|
||||||
|
|
||||||
|
if(seq_scaling_list_present_flag) {
|
||||||
|
int sizeOfScalingList = (i < 6) ? 16 : 64;
|
||||||
|
int lastScale = 8;
|
||||||
|
int nextScale = 8;
|
||||||
|
int j = 0;
|
||||||
|
for(j = 0; j < sizeOfScalingList; j++) {
|
||||||
|
if(nextScale != 0) {
|
||||||
|
int delta_scale;
|
||||||
|
ZOOOZOOO_SE(delta_scale);
|
||||||
|
|
||||||
|
nextScale = (lastScale + delta_scale + 256) % 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastScale = (nextScale == 0) ? lastScale : nextScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int log2_max_frame_num_minus4;
|
||||||
|
ZOOOZOOO_UE(log2_max_frame_num_minus4);
|
||||||
|
|
||||||
|
int pic_order_cnt_type;
|
||||||
|
ZOOOZOOO_UE(pic_order_cnt_type);
|
||||||
|
|
||||||
|
if(pic_order_cnt_type == 0) {
|
||||||
|
int log2_max_pic_order_cnt_lsb_minus4;
|
||||||
|
ZOOOZOOO_UE(log2_max_pic_order_cnt_lsb_minus4);
|
||||||
|
} else if(pic_order_cnt_type == 1) {
|
||||||
|
int delta_pic_order_always_zero_flag;
|
||||||
|
ZOOOZOOO_BIT(delta_pic_order_always_zero_flag);
|
||||||
|
|
||||||
|
int offset_for_non_ref_pic;
|
||||||
|
ZOOOZOOO_SE(offset_for_non_ref_pic);
|
||||||
|
|
||||||
|
int offset_for_top_to_bottom_field;
|
||||||
|
ZOOOZOOO_SE(offset_for_top_to_bottom_field);
|
||||||
|
|
||||||
|
int num_ref_frames_in_pic_order_cnt_cycle;
|
||||||
|
ZOOOZOOO_UE(num_ref_frames_in_pic_order_cnt_cycle);
|
||||||
|
|
||||||
|
for(int i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) {
|
||||||
|
int offset_for_ref_frame_i;
|
||||||
|
ZOOOZOOO_SE(offset_for_ref_frame_i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int max_num_ref_frames;
|
||||||
|
ZOOOZOOO_UE(max_num_ref_frames);
|
||||||
|
|
||||||
|
int gaps_in_frame_num_value_allowed_flag;
|
||||||
|
ZOOOZOOO_BIT(gaps_in_frame_num_value_allowed_flag);
|
||||||
|
|
||||||
|
int pic_width_in_mbs_minus1;
|
||||||
|
ZOOOZOOO_UE(pic_width_in_mbs_minus1);
|
||||||
|
|
||||||
|
int pic_height_in_map_units_minus1;
|
||||||
|
ZOOOZOOO_UE(pic_height_in_map_units_minus1);
|
||||||
|
|
||||||
|
int frame_mbs_only_flag;
|
||||||
|
ZOOOZOOO_BIT(frame_mbs_only_flag);
|
||||||
|
|
||||||
|
if(!frame_mbs_only_flag) {
|
||||||
|
int mb_adaptive_frame_field_flag;
|
||||||
|
ZOOOZOOO_BIT(mb_adaptive_frame_field_flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
int direct_8x8_inference_flag;
|
||||||
|
ZOOOZOOO_BIT(direct_8x8_inference_flag);
|
||||||
|
|
||||||
|
int frame_cropping_flag;
|
||||||
|
ZOOOZOOO_BIT(frame_cropping_flag);
|
||||||
|
|
||||||
|
int frame_crop_left_offset = 0;
|
||||||
|
int frame_crop_right_offset = 0;
|
||||||
|
int frame_crop_top_offset = 0;
|
||||||
|
int frame_crop_bottom_offset = 0;
|
||||||
|
if(frame_cropping_flag) {
|
||||||
|
ZOOOZOOO_UE(frame_crop_left_offset);
|
||||||
|
ZOOOZOOO_UE(frame_crop_right_offset);
|
||||||
|
ZOOOZOOO_UE(frame_crop_top_offset);
|
||||||
|
ZOOOZOOO_UE(frame_crop_bottom_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
int vui_parameters_present_flag;
|
||||||
|
ZOOOZOOO_BIT(vui_parameters_present_flag);
|
||||||
|
|
||||||
|
*foundWidth = ((pic_width_in_mbs_minus1 + 1) * 16) - frame_crop_bottom_offset * 2 - frame_crop_top_offset * 2;
|
||||||
|
*foundHeight = (2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1) * 16 - frame_crop_right_offset * 2 - frame_crop_left_offset * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t *annexb_to_extradata(const uint8_t *src, const uint8_t *srcEnd, size_t *szRet, size_t *srcSzEnd) {
|
||||||
|
const uint8_t *sps = src;
|
||||||
|
while(*sps == 0) sps++;
|
||||||
|
assert(sps[0] == 1);
|
||||||
|
sps++;
|
||||||
|
|
||||||
|
size_t szSps = annexb_parse(sps, srcEnd);
|
||||||
|
|
||||||
|
const uint8_t *pps = sps + szSps;
|
||||||
|
while(*pps == 0) pps++;
|
||||||
|
assert(pps[0] == 1);
|
||||||
|
pps++;
|
||||||
|
|
||||||
|
size_t szPps = annexb_parse(pps, srcEnd);
|
||||||
|
|
||||||
|
uint8_t *ret = malloc(*szRet = (6 + 2 + szSps + 1 + 2 + szPps));
|
||||||
|
ret[0] = 1;
|
||||||
|
ret[1] = sps[1];
|
||||||
|
ret[2] = sps[2];
|
||||||
|
ret[3] = sps[3];
|
||||||
|
ret[4] = 0xFC | (NALLENSZ - 1);
|
||||||
|
|
||||||
|
ret[5] = 0xE0 | 1;
|
||||||
|
ret[6] = szSps >> 8;
|
||||||
|
ret[7] = szSps & 0xFF;
|
||||||
|
|
||||||
|
memcpy(&ret[8], sps, szSps);
|
||||||
|
|
||||||
|
ret[8 + szSps + 0] = 1;
|
||||||
|
ret[8 + szSps + 1] = szPps >> 8;
|
||||||
|
ret[8 + szSps + 2] = szPps & 0xFF;
|
||||||
|
|
||||||
|
memcpy(&ret[8 + szSps + 3], pps, szPps);
|
||||||
|
|
||||||
|
*srcSzEnd = pps + szPps - src;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t *annexb_to_avcc(const uint8_t *src, size_t szSrc, size_t *szRet) {
|
||||||
|
size_t cap = 4096, sz = 0;
|
||||||
|
uint8_t *ret = malloc(cap);
|
||||||
|
|
||||||
|
const uint8_t *srcEnd = src + szSrc;
|
||||||
|
while(src != srcEnd) {
|
||||||
|
assert(*src == 0);
|
||||||
|
while(*src == 0) {
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
assert(*src == 1);
|
||||||
|
src++;
|
||||||
|
|
||||||
|
size_t nalSize = annexb_parse(src, srcEnd);
|
||||||
|
|
||||||
|
size_t additionSz = NALLENSZ + nalSize;
|
||||||
|
|
||||||
|
if(sz + additionSz > cap) {
|
||||||
|
ret = realloc(ret, cap = (sz + additionSz));
|
||||||
|
}
|
||||||
|
|
||||||
|
*(uint32_t*) &ret[sz] = htonl(nalSize);
|
||||||
|
memcpy(&ret[sz + NALLENSZ], src, nalSize);
|
||||||
|
|
||||||
|
sz += additionSz;
|
||||||
|
src += nalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
*szRet = sz;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Internal {
|
||||||
|
CHiPubNode pub;
|
||||||
|
|
||||||
|
FILE *fd;
|
||||||
|
EBMLWriter wr;
|
||||||
|
|
||||||
|
size_t videoTrack, audioTrack;
|
||||||
|
|
||||||
|
size_t currentClusterTimecode;
|
||||||
|
|
||||||
|
bool codecPrivatesFound;
|
||||||
|
|
||||||
|
CHiBSFrames *audioBacklog;
|
||||||
|
CHiBSFrames *videoBacklog;
|
||||||
|
};
|
||||||
|
|
||||||
|
static size_t next_timestamp(struct Internal *this) {
|
||||||
|
if(this->videoBacklog && this->videoBacklog->count > 0 && (!this->audioBacklog || (this->audioBacklog->count > 0 && this->videoBacklog->data[0].timestamp <= this->audioBacklog->data[0].timestamp))) {
|
||||||
|
return this->videoBacklog->data[0].timestamp;
|
||||||
|
} else if(this->audioBacklog && this->audioBacklog->count > 0 && (!this->videoBacklog || (this->videoBacklog->count > 0 && this->audioBacklog->data[0].timestamp <= this->videoBacklog->data[0].timestamp))) {
|
||||||
|
return this->audioBacklog->data[0].timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int muxmkv_perform(CHiPubNode *pubn) {
|
||||||
|
MTR_BEGIN("CHi", "muxmkv_perform");
|
||||||
|
|
||||||
|
struct Internal *this = (void*) pubn;
|
||||||
|
|
||||||
|
if(pubn->sinks[0].data.linked.to) {
|
||||||
|
CHiBSFrames *vp9bs = CHi_Crawl(&pubn->sinks[0])->data.bitstream;
|
||||||
|
if(vp9bs) {
|
||||||
|
this->videoBacklog = CHi_BS_Combine(this->videoBacklog, vp9bs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pubn->sinks[1].data.linked.to) {
|
||||||
|
CHiBSFrames *opus = CHi_Crawl(&pubn->sinks[1])->data.bitstream;
|
||||||
|
if(opus) {
|
||||||
|
this->audioBacklog = CHi_BS_Combine(this->audioBacklog, opus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->codecPrivatesFound) {
|
||||||
|
while(
|
||||||
|
(!this->audioBacklog || this->audioBacklog->count > 0) &&
|
||||||
|
(!this->videoBacklog || this->videoBacklog->count > 0)) {
|
||||||
|
|
||||||
|
size_t nextTimestamp = next_timestamp(this);
|
||||||
|
|
||||||
|
bool shouldUpdateCluster = (this->videoBacklog && this->videoBacklog->count && (this->videoBacklog->data[0].flags & CUTIHI_BS_FLAG_KEY)) || (nextTimestamp - this->currentClusterTimecode > 15000);
|
||||||
|
|
||||||
|
if(shouldUpdateCluster) {
|
||||||
|
if(this->currentClusterTimecode != 0) {
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cluster
|
||||||
|
ebml_writer_push(&this->wr, 0x1F43B675);
|
||||||
|
|
||||||
|
this->currentClusterTimecode = nextTimestamp;
|
||||||
|
|
||||||
|
// Timecode
|
||||||
|
ebml_writer_put(&this->wr, 0xE7, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = this->currentClusterTimecode});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->videoBacklog && this->videoBacklog->count && (!this->audioBacklog || (this->audioBacklog->count > 0 && this->videoBacklog->data[0].timestamp <= this->audioBacklog->data[0].timestamp))) {
|
||||||
|
CHiBSFrame *frame = &this->videoBacklog->data[0];
|
||||||
|
|
||||||
|
assert((frame->flags & CUTIHI_BS_SETUP_PACKET) == 0);
|
||||||
|
|
||||||
|
size_t avccSz = 0;
|
||||||
|
uint8_t *avcc = annexb_to_avcc(frame->ptr, frame->sz, &avccSz);
|
||||||
|
|
||||||
|
size_t simpleBlockSize = 4 + avccSz;
|
||||||
|
|
||||||
|
uint8_t *simpleBlock = malloc(simpleBlockSize);
|
||||||
|
simpleBlock[0] = 0x80 | this->videoTrack;
|
||||||
|
*(uint16_t*) &simpleBlock[1] = __builtin_bswap16(frame->timestamp - this->currentClusterTimecode);
|
||||||
|
simpleBlock[3] = (frame->flags & CUTIHI_BS_FLAG_KEY) ? 128 : 0;
|
||||||
|
|
||||||
|
memcpy(simpleBlock + 4, avcc, avccSz);
|
||||||
|
|
||||||
|
// SimpleBlock
|
||||||
|
ebml_writer_put(&this->wr, 0xA3, EBML_BINARY, (EBMLPrimitive) {.binary = {.length = simpleBlockSize, .ptr = simpleBlock}});
|
||||||
|
|
||||||
|
free(simpleBlock);
|
||||||
|
free(avcc);
|
||||||
|
|
||||||
|
CHi_BS_Pop(this->videoBacklog, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->audioBacklog && this->audioBacklog->count && (!this->videoBacklog || (this->videoBacklog->count > 0 && this->audioBacklog->data[0].timestamp <= this->videoBacklog->data[0].timestamp))) {
|
||||||
|
CHiBSFrame *frame = &this->audioBacklog->data[0];
|
||||||
|
|
||||||
|
assert((frame->flags & CUTIHI_BS_SETUP_PACKET) == 0);
|
||||||
|
|
||||||
|
size_t simpleBlockSize = 4 + frame->sz;
|
||||||
|
|
||||||
|
uint8_t *simpleBlock = malloc(simpleBlockSize);
|
||||||
|
simpleBlock[0] = 0x80 | this->audioTrack;
|
||||||
|
*(uint16_t*) &simpleBlock[1] = __builtin_bswap16(frame->timestamp - this->currentClusterTimecode);
|
||||||
|
simpleBlock[3] = (frame->flags & CUTIHI_BS_FLAG_KEY) ? 128 : 0;
|
||||||
|
|
||||||
|
memcpy(simpleBlock + 4, frame->ptr, frame->sz);
|
||||||
|
|
||||||
|
// SimpleBlock
|
||||||
|
ebml_writer_put(&this->wr, 0xA3, EBML_BINARY, (EBMLPrimitive) {.binary = {.length = simpleBlockSize, .ptr = simpleBlock}});
|
||||||
|
|
||||||
|
free(simpleBlock);
|
||||||
|
|
||||||
|
CHi_BS_Pop(this->audioBacklog, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if((!this->audioBacklog || this->audioBacklog->count) && (!this->videoBacklog || this->videoBacklog->count)) {
|
||||||
|
uint8_t randbuf[16];
|
||||||
|
|
||||||
|
// Segment
|
||||||
|
ebml_writer_push_ind(&this->wr, 0x18538067);
|
||||||
|
|
||||||
|
// Info
|
||||||
|
ebml_writer_push(&this->wr, 0x1549A966);
|
||||||
|
// SegmentUUID
|
||||||
|
getrandom(randbuf, sizeof(randbuf), 0);
|
||||||
|
ebml_writer_put(&this->wr, 0x73A4, EBML_BINARY, (EBMLPrimitive) {.binary = {.ptr = randbuf, .length = sizeof(randbuf)}});
|
||||||
|
|
||||||
|
// TimestampScale
|
||||||
|
ebml_writer_put(&this->wr, 0x2AD7B1, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 1000000});
|
||||||
|
|
||||||
|
// MuxingApp
|
||||||
|
ebml_writer_put(&this->wr, 0x4D80, EBML_STRING, (EBMLPrimitive) {.string = "cuticle"});
|
||||||
|
|
||||||
|
// WritingApp
|
||||||
|
ebml_writer_put(&this->wr, 0x5741, EBML_STRING, (EBMLPrimitive) {.string = "cuticle"});
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
|
||||||
|
// Tracks
|
||||||
|
ebml_writer_push(&this->wr, 0x1654AE6B);
|
||||||
|
if(this->videoBacklog) {
|
||||||
|
// TrackEntry
|
||||||
|
ebml_writer_push(&this->wr, 0xAE);
|
||||||
|
// TrackNumber
|
||||||
|
ebml_writer_put(&this->wr, 0xD7, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = this->videoTrack});
|
||||||
|
|
||||||
|
// TrackUID
|
||||||
|
getrandom(randbuf, sizeof(uint64_t), 0);
|
||||||
|
ebml_writer_put(&this->wr, 0x73C5, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = *(uint64_t*) &randbuf[0]});
|
||||||
|
|
||||||
|
// TrackType
|
||||||
|
ebml_writer_put(&this->wr, 0x83, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 1});
|
||||||
|
|
||||||
|
// FlagLacing
|
||||||
|
ebml_writer_put(&this->wr, 0x9C, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 0});
|
||||||
|
|
||||||
|
CHiBSFrame *setup = &this->videoBacklog->data[0];
|
||||||
|
|
||||||
|
assert(setup->flags & CUTIHI_BS_SETUP_PACKET);
|
||||||
|
|
||||||
|
uint16_t width = 0, height = 0;
|
||||||
|
|
||||||
|
if(CHi_Crawl(&pubn->sinks[0])->type == CUTIHI_VAL_VP8BS) {
|
||||||
|
ebml_writer_put(&this->wr, 0x86, EBML_STRING, (EBMLPrimitive) {.string = "V_VP8"});
|
||||||
|
|
||||||
|
CHiVPxBSSettings *settings = setup->ptr;
|
||||||
|
width = settings->width;
|
||||||
|
height = settings->height;
|
||||||
|
|
||||||
|
setup = NULL;
|
||||||
|
CHi_BS_Pop(this->videoBacklog, 1);
|
||||||
|
} else if(CHi_Crawl(&pubn->sinks[0])->type == CUTIHI_VAL_VP9BS) {
|
||||||
|
ebml_writer_put(&this->wr, 0x86, EBML_STRING, (EBMLPrimitive) {.string = "V_VP9"});
|
||||||
|
|
||||||
|
CHiVPxBSSettings *settings = setup->ptr;
|
||||||
|
width = settings->width;
|
||||||
|
height = settings->height;
|
||||||
|
|
||||||
|
setup = NULL;
|
||||||
|
CHi_BS_Pop(this->videoBacklog, 1);
|
||||||
|
} else if(CHi_Crawl(&pubn->sinks[0])->type == CUTIHI_VAL_H264BS) {
|
||||||
|
ebml_writer_put(&this->wr, 0x86, EBML_STRING, (EBMLPrimitive) {.string = "V_MPEG4/ISO/AVC"});
|
||||||
|
|
||||||
|
size_t szRet, srcSzEnd;
|
||||||
|
uint8_t *extradata = annexb_to_extradata(setup->ptr, setup->ptr + setup->sz, &szRet, &srcSzEnd);
|
||||||
|
|
||||||
|
// CodecPrivate
|
||||||
|
ebml_writer_put(&this->wr, 0x63A2, EBML_BINARY, (EBMLPrimitive) {.binary = {.length = szRet, .ptr = extradata}});
|
||||||
|
|
||||||
|
get_dimensions_from_extradata(extradata, &width, &height);
|
||||||
|
|
||||||
|
free(extradata);
|
||||||
|
|
||||||
|
// Frame still contains keyframe because of legacy crap
|
||||||
|
memmove(setup->ptr, setup->ptr + srcSzEnd, setup->sz - srcSzEnd);
|
||||||
|
setup->sz -= srcSzEnd;
|
||||||
|
setup->flags &= ~CUTIHI_BS_SETUP_PACKET;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(width > 0 && height > 0);
|
||||||
|
|
||||||
|
// Video
|
||||||
|
ebml_writer_push(&this->wr, 0xE0);
|
||||||
|
// PixelWidth
|
||||||
|
ebml_writer_put(&this->wr, 0xB0, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = width});
|
||||||
|
|
||||||
|
// PixelHeight
|
||||||
|
ebml_writer_put(&this->wr, 0xBA, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = height});
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->audioBacklog) {
|
||||||
|
// TrackEntry
|
||||||
|
ebml_writer_push(&this->wr, 0xAE);
|
||||||
|
// TrackNumber
|
||||||
|
ebml_writer_put(&this->wr, 0xD7, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = this->audioTrack});
|
||||||
|
|
||||||
|
// TrackUID
|
||||||
|
getrandom(randbuf, sizeof(uint64_t), 0);
|
||||||
|
ebml_writer_put(&this->wr, 0x73C5, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = *(uint64_t*) &randbuf[0]});
|
||||||
|
|
||||||
|
// TrackType
|
||||||
|
ebml_writer_put(&this->wr, 0x83, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 2});
|
||||||
|
|
||||||
|
// FlagLacing
|
||||||
|
ebml_writer_put(&this->wr, 0x9C, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 0});
|
||||||
|
|
||||||
|
assert(this->audioBacklog->data[0].flags & CUTIHI_BS_SETUP_PACKET);
|
||||||
|
|
||||||
|
if(CHi_Crawl(&pubn->sinks[0])->type == CUTIHI_VAL_OPUSBS) {
|
||||||
|
ebml_writer_put(&this->wr, 0x86, EBML_STRING, (EBMLPrimitive) {.string = "A_OPUS"});
|
||||||
|
|
||||||
|
CHiBSFrame *opusHead = &this->audioBacklog->data[0];
|
||||||
|
|
||||||
|
// CodecPrivate
|
||||||
|
ebml_writer_put(&this->wr, 0x63A2, EBML_BINARY, (EBMLPrimitive) {.binary = {.length = opusHead->sz, .ptr = opusHead->ptr}});
|
||||||
|
} else if(CHi_Crawl(&pubn->sinks[0])->type == CUTIHI_VAL_AACBS) {
|
||||||
|
ebml_writer_put(&this->wr, 0x86, EBML_STRING, (EBMLPrimitive) {.string = "A_AAC/MPEG2/LC"});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audio
|
||||||
|
ebml_writer_push(&this->wr, 0xE1);
|
||||||
|
// SamplingFrequency
|
||||||
|
ebml_writer_put(&this->wr, 0xB0, EBML_FLOAT4, (EBMLPrimitive) {.flt4 = 48000});
|
||||||
|
|
||||||
|
// Channels
|
||||||
|
ebml_writer_put(&this->wr, 0x9F, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 1});
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
|
||||||
|
CHi_BS_Pop(this->audioBacklog, 1);
|
||||||
|
}
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
|
||||||
|
this->codecPrivatesFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
MTR_END("CHi", "muxmkv_perform");
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
static void muxmkv_destroy(CHiPubNode *pubn) {
|
||||||
|
struct Internal *this = (void*) pubn;
|
||||||
|
|
||||||
|
CHi_BS_Free(this->audioBacklog);
|
||||||
|
this->audioBacklog = NULL;
|
||||||
|
|
||||||
|
CHi_BS_Free(this->videoBacklog);
|
||||||
|
this->videoBacklog = NULL;
|
||||||
|
|
||||||
|
free(this);
|
||||||
|
}
|
||||||
|
static size_t ebml_write(EBMLWriter *this, const void *data, size_t length) {
|
||||||
|
return fwrite(data, 1, length, this->ud);
|
||||||
|
}
|
||||||
|
static void *ebml_alloc(EBMLWriter *this, void *data, size_t length) {
|
||||||
|
return realloc(data, length);
|
||||||
|
}
|
||||||
|
static int muxmkv_start(CHiPubNode *pubn) {
|
||||||
|
struct Internal *this = (void*) pubn;
|
||||||
|
|
||||||
|
this->currentClusterTimecode = 0;
|
||||||
|
this->codecPrivatesFound = false;
|
||||||
|
|
||||||
|
int trackNum = 1;
|
||||||
|
|
||||||
|
if(pubn->sinks[0].data.linked.to) {
|
||||||
|
this->videoBacklog = CHi_BS_Empty();
|
||||||
|
this->videoTrack = trackNum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pubn->sinks[1].data.linked.to) {
|
||||||
|
this->audioBacklog = CHi_BS_Empty();
|
||||||
|
this->audioTrack = trackNum++;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->fd = fopen(CHi_Crawl(&pubn->sinks[CUTIHI_MUXWEBM_IN_FILENAME])->data.text, "wb");
|
||||||
|
|
||||||
|
if(!this->fd) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ebml_writer_init(&this->wr);
|
||||||
|
|
||||||
|
this->wr.ud = this->fd;
|
||||||
|
this->wr.eventWrite = ebml_write;
|
||||||
|
this->wr.eventAlloc = ebml_alloc;
|
||||||
|
|
||||||
|
ebml_writer_push(&this->wr, 0x1A45DFA3);
|
||||||
|
ebml_writer_put(&this->wr, 0x4282, EBML_STRING, (EBMLPrimitive) {.string = "matroska"});
|
||||||
|
ebml_writer_put(&this->wr, 0x42F2, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 4});
|
||||||
|
ebml_writer_put(&this->wr, 0x42F3, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 8});
|
||||||
|
ebml_writer_put(&this->wr, 0x4287, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 2});
|
||||||
|
ebml_writer_put(&this->wr, 0x4285, EBML_UNSIGNED_INTEGER, (EBMLPrimitive) {.uInt = 2});
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
static int muxmkv_stop(CHiPubNode *pubn) {
|
||||||
|
struct Internal *this = (void*) pubn;
|
||||||
|
|
||||||
|
while(this->wr.currentDepth) {
|
||||||
|
ebml_writer_pop(&this->wr);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(!ebml_writer_flush(&this->wr));
|
||||||
|
|
||||||
|
ebml_writer_free(&this->wr);
|
||||||
|
|
||||||
|
fclose(this->fd);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
CUTIVIS CHiPubNode *CHi_MuxMatroska() {
|
||||||
|
struct Internal *n = calloc(1, sizeof(*n));
|
||||||
|
n->pub.type = CUTIHI_T('CExp','Mkv ');
|
||||||
|
n->pub.Start = muxmkv_start;
|
||||||
|
n->pub.Perform = muxmkv_perform;
|
||||||
|
n->pub.Destroy = muxmkv_destroy;
|
||||||
|
n->pub.Stop = muxmkv_stop;
|
||||||
|
n->pub.sinks = calloc(sizeof(*n->pub.sinks), n->pub.sinkCount = 3);
|
||||||
|
n->pub.sourceCount = 0;
|
||||||
|
n->pub.sources = NULL;
|
||||||
|
return &n->pub;
|
||||||
|
}
|
@ -1528,6 +1528,8 @@ CUTIVIS int CHi_NodeGraphLoad(CHiNodeGraph *ng, CHiLoadReader reader, void *ud)
|
|||||||
n = CHi_StreamRTMP();
|
n = CHi_StreamRTMP();
|
||||||
} else if(type == CUTIHI_T('CEnc','AACL')) {
|
} else if(type == CUTIHI_T('CEnc','AACL')) {
|
||||||
n = CHi_EncodeAAC();
|
n = CHi_EncodeAAC();
|
||||||
|
} else if(type == CUTIHI_T('CExp','Mkv ')) {
|
||||||
|
n = CHi_MuxMatroska();
|
||||||
}
|
}
|
||||||
|
|
||||||
n->ng = ng;
|
n->ng = ng;
|
||||||
|
@ -275,6 +275,8 @@ CUTIVIS CHiPubNode *CHi_StreamRTMP();
|
|||||||
|
|
||||||
CUTIVIS CHiPubNode *CHi_EncodeAAC();
|
CUTIVIS CHiPubNode *CHi_EncodeAAC();
|
||||||
|
|
||||||
|
CUTIVIS CHiPubNode *CHi_MuxMatroska();
|
||||||
|
|
||||||
CUTIVIS CHiValue *CHi_Crawl(CHiValue*);
|
CUTIVIS CHiValue *CHi_Crawl(CHiValue*);
|
||||||
|
|
||||||
//CUTIVIS void CHi_Save(CHiNodeGraph *ng);
|
//CUTIVIS void CHi_Save(CHiNodeGraph *ng);
|
||||||
|
38
hi/opus.c
38
hi/opus.c
@ -14,6 +14,8 @@ struct CHiEncodeOpusNode {
|
|||||||
|
|
||||||
size_t timestamp;
|
size_t timestamp;
|
||||||
|
|
||||||
|
bool firstFrame;
|
||||||
|
|
||||||
OpusEncoder *enc;
|
OpusEncoder *enc;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -34,14 +36,38 @@ static int encodeopus_perform(CHiPubNode *pubn) {
|
|||||||
CHiBSFrames *frames = malloc(sizeof(*frames));
|
CHiBSFrames *frames = malloc(sizeof(*frames));
|
||||||
frames->count = 0;
|
frames->count = 0;
|
||||||
|
|
||||||
|
if(!n->firstFrame) {
|
||||||
|
frames = CHi_BS_Grow(frames, 1);
|
||||||
|
|
||||||
|
struct OpusHead {
|
||||||
|
uint8_t magic[8];
|
||||||
|
uint8_t version;
|
||||||
|
uint8_t channelCount;
|
||||||
|
uint16_t preSkip;
|
||||||
|
uint32_t inputSampleRate;
|
||||||
|
uint16_t outputGain;
|
||||||
|
uint8_t mappingFamily;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct OpusHead *head = calloc(1, sizeof(*head));
|
||||||
|
*head = (struct OpusHead) {{'O', 'p', 'u', 's', 'H', 'e', 'a', 'd'}, 1, 1, 3840, 48000, 0, 0};
|
||||||
|
|
||||||
|
frames->data[0].timestamp = 0;
|
||||||
|
frames->data[0].flags = CUTIHI_BS_SETUP_PACKET;
|
||||||
|
frames->data[0].ptr = head;
|
||||||
|
frames->data[0].sz = sizeof(*head);
|
||||||
|
|
||||||
|
n->firstFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
size_t samp;
|
size_t samp;
|
||||||
for(samp = 0; samp + 960 <= n->pcmSamples; samp += 960) {
|
for(samp = 0; samp + 960 <= n->pcmSamples; samp += 960) {
|
||||||
frames = realloc(frames, sizeof(*frames) + sizeof(*frames->data) * (frames->count + 1));
|
frames = CHi_BS_Grow(frames, 1);
|
||||||
frames->data[frames->count].timestamp = n->timestamp;
|
|
||||||
frames->data[frames->count].ptr = malloc(1276);
|
frames->data[frames->count - 1].timestamp = n->timestamp;
|
||||||
frames->data[frames->count].sz = opus_encode(n->enc, &n->pcmbuf[samp], 960, frames->data[frames->count].ptr, 1276);
|
frames->data[frames->count - 1].ptr = malloc(1276);
|
||||||
if(frames->data[frames->count].sz < 0) puts("OPUS ERR");
|
frames->data[frames->count - 1].sz = opus_encode(n->enc, &n->pcmbuf[samp], 960, frames->data[frames->count - 1].ptr, 1276);
|
||||||
frames->count++;
|
if(frames->data[frames->count - 1].sz < 0) puts("OPUS ERR");
|
||||||
|
|
||||||
n->timestamp += 20;
|
n->timestamp += 20;
|
||||||
}
|
}
|
||||||
|
33
hi/vpxenc.c
33
hi/vpxenc.c
@ -32,6 +32,8 @@ typedef struct CHiEncodeVP9Node {
|
|||||||
uint8_t *outY, *outU, *outV;
|
uint8_t *outY, *outU, *outV;
|
||||||
uint16_t strideY, strideU, strideV;
|
uint16_t strideY, strideU, strideV;
|
||||||
|
|
||||||
|
bool firstFrame;
|
||||||
|
|
||||||
vpx_codec_iface_t *iface;
|
vpx_codec_iface_t *iface;
|
||||||
} CHiEncodeVP9Node;
|
} CHiEncodeVP9Node;
|
||||||
|
|
||||||
@ -70,20 +72,33 @@ static int encodevpx_perform(CHiPubNode *pub) {
|
|||||||
|
|
||||||
vpx_codec_encode(&node->codec, &vpxraw, CHi_Time_Get(pub->ng) * 1000.f, 1, 0, VPX_DL_REALTIME);
|
vpx_codec_encode(&node->codec, &vpxraw, CHi_Time_Get(pub->ng) * 1000.f, 1, 0, VPX_DL_REALTIME);
|
||||||
|
|
||||||
CHiBSFrames *ret = malloc(sizeof(CHiBSFrames));
|
CHiBSFrames *ret = calloc(1, sizeof(CHiBSFrames));
|
||||||
ret->count = 0;
|
|
||||||
|
if(!node->firstFrame) {
|
||||||
|
ret = CHi_BS_Grow(ret, 1);
|
||||||
|
|
||||||
|
CHiVPxBSSettings *s = calloc(1, sizeof(*s));
|
||||||
|
s->width = vpxraw.w;
|
||||||
|
s->height = vpxraw.h;
|
||||||
|
|
||||||
|
ret->data[0].timestamp = 0;
|
||||||
|
ret->data[0].sz = sizeof(CHiVPxBSSettings);
|
||||||
|
ret->data[0].flags = CUTIHI_BS_SETUP_PACKET;
|
||||||
|
ret->data[0].ptr = s;
|
||||||
|
|
||||||
|
node->firstFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
vpx_codec_iter_t iter = NULL;
|
vpx_codec_iter_t iter = NULL;
|
||||||
const vpx_codec_cx_pkt_t *pkt;
|
const vpx_codec_cx_pkt_t *pkt;
|
||||||
while((pkt = vpx_codec_get_cx_data(&node->codec, &iter)) != NULL) {
|
while((pkt = vpx_codec_get_cx_data(&node->codec, &iter)) != NULL) {
|
||||||
if(pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
|
if(pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
|
||||||
ret = (CHiBSFrames*) realloc(ret, sizeof(CHiBSFrames) + sizeof(CHiBSFrame) * (ret->count + 1));
|
ret = CHi_BS_Grow(ret, 1);
|
||||||
ret->data[ret->count].timestamp = pkt->data.frame.pts;
|
ret->data[ret->count - 1].timestamp = pkt->data.frame.pts;
|
||||||
ret->data[ret->count].sz = pkt->data.frame.sz;
|
ret->data[ret->count - 1].sz = pkt->data.frame.sz;
|
||||||
ret->data[ret->count].flags = pkt->data.frame.flags & VPX_FRAME_IS_KEY;
|
ret->data[ret->count - 1].flags = pkt->data.frame.flags & VPX_FRAME_IS_KEY;
|
||||||
ret->data[ret->count].ptr = malloc(ret->data[ret->count].sz);
|
ret->data[ret->count - 1].ptr = malloc(ret->data[ret->count - 1].sz);
|
||||||
memcpy(ret->data[ret->count].ptr, pkt->data.frame.buf, ret->data[ret->count].sz);
|
memcpy(ret->data[ret->count - 1].ptr, pkt->data.frame.buf, ret->data[ret->count - 1].sz);
|
||||||
ret->count++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
ui/frame.cpp
16
ui/frame.cpp
@ -38,7 +38,7 @@ std::string node_name_from_id(uint64_t id) {
|
|||||||
{CUTIHI_T('CTim','e '), "Time"},
|
{CUTIHI_T('CTim','e '), "Time"},
|
||||||
{CUTIHI_T('CMod','ulat'), "Modulate"},
|
{CUTIHI_T('CMod','ulat'), "Modulate"},
|
||||||
{CUTIHI_T('CCns','tCol'), "Constant"},
|
{CUTIHI_T('CCns','tCol'), "Constant"},
|
||||||
{CUTIHI_T('CEmb','ed '), "Frame"},
|
{CUTIHI_T('CEmb','ed '), "Layer"},
|
||||||
{CUTIHI_T('CIma','ge '), "Image"},
|
{CUTIHI_T('CIma','ge '), "Image"},
|
||||||
{CUTIHI_T('CWin','dow '), "Window"},
|
{CUTIHI_T('CWin','dow '), "Window"},
|
||||||
{CUTIHI_T('CInA','udio'), "Microphone"},
|
{CUTIHI_T('CInA','udio'), "Microphone"},
|
||||||
@ -55,6 +55,7 @@ std::string node_name_from_id(uint64_t id) {
|
|||||||
{CUTIHI_T('CChr','omaK'), "Chroma Key"},
|
{CUTIHI_T('CChr','omaK'), "Chroma Key"},
|
||||||
{CUTIHI_T('CStr','RTMP'), "Stream RTMP"},
|
{CUTIHI_T('CStr','RTMP'), "Stream RTMP"},
|
||||||
{CUTIHI_T('CEnc','AACL'), "Encode AAC-LC"},
|
{CUTIHI_T('CEnc','AACL'), "Encode AAC-LC"},
|
||||||
|
{CUTIHI_T('CExp','Mkv '), "Mux Matroska"},
|
||||||
};
|
};
|
||||||
|
|
||||||
auto nameit = NODE_ID_NAMES.find(id);
|
auto nameit = NODE_ID_NAMES.find(id);
|
||||||
@ -108,7 +109,7 @@ static void ShapeGrNode(GrNode *gn) {
|
|||||||
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
|
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
|
||||||
} else if(gn->logical->type == CUTIHI_T('CEmb','ed ')) {
|
} else if(gn->logical->type == CUTIHI_T('CEmb','ed ')) {
|
||||||
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
|
gn->sources = {{"Sample", GrNode::Port::Type::SAMPLE}};
|
||||||
gn->sinks = {{"Frame", GrNode::Port::Type::SAMPLE, true}};
|
gn->sinks = {{"Background", GrNode::Port::Type::SAMPLE, true}};
|
||||||
|
|
||||||
int numSubs = 0;
|
int numSubs = 0;
|
||||||
while(gn->logical->sinkCount > (1 + numSubs * 3 + 0) && gn->logical->sinks[1 + numSubs * 3 + 0].type != CUTIHI_VAL_NONE) {
|
while(gn->logical->sinkCount > (1 + numSubs * 3 + 0) && gn->logical->sinks[1 + numSubs * 3 + 0].type != CUTIHI_VAL_NONE) {
|
||||||
@ -169,6 +170,9 @@ static void ShapeGrNode(GrNode *gn) {
|
|||||||
} else if(gn->logical->type == CUTIHI_T('CEnc','AACL')) {
|
} else if(gn->logical->type == CUTIHI_T('CEnc','AACL')) {
|
||||||
gn->sinks = {{"Audio", GrNode::Port::Type::SAMPLE}};
|
gn->sinks = {{"Audio", GrNode::Port::Type::SAMPLE}};
|
||||||
gn->sources = {{"Bitstream"}};
|
gn->sources = {{"Bitstream"}};
|
||||||
|
} else if(gn->logical->type == CUTIHI_T('CExp','Mkv ')) {
|
||||||
|
gn->sinks = {{"Video Bitstream"}, {"Audio Bitstream"}, {"Filename", GrNode::Port::Type::FILE_SAVE}};
|
||||||
|
gn->sources = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
gn->Fit();
|
gn->Fit();
|
||||||
@ -922,7 +926,7 @@ NodeGraph::NodeGraph(Frame *f) : wxPanel(f, wxID_ANY) {
|
|||||||
int idEncodeVp9 = menuExports->Append(wxID_ANY, "Encode VP9", "")->GetId();
|
int idEncodeVp9 = menuExports->Append(wxID_ANY, "Encode VP9", "")->GetId();
|
||||||
int idEncodeOpus = menuExports->Append(wxID_ANY, "Encode Opus", "")->GetId();
|
int idEncodeOpus = menuExports->Append(wxID_ANY, "Encode Opus", "")->GetId();
|
||||||
int idEncodeAAC = menuExports->Append(wxID_ANY, "Encode AAC-LC", "")->GetId();
|
int idEncodeAAC = menuExports->Append(wxID_ANY, "Encode AAC-LC", "")->GetId();
|
||||||
int idMuxWebm = menuExports->Append(wxID_ANY, "Mux WebM", "")->GetId();
|
int idMuxMatroska = menuExports->Append(wxID_ANY, "Mux Matroska", "")->GetId();
|
||||||
int idMuxWav = menuExports->Append(wxID_ANY, "Muv Wav", "")->GetId();
|
int idMuxWav = menuExports->Append(wxID_ANY, "Muv Wav", "")->GetId();
|
||||||
int idStreamRTMP = menuExports->Append(wxID_ANY, "Stream RTMP", "")->GetId();
|
int idStreamRTMP = menuExports->Append(wxID_ANY, "Stream RTMP", "")->GetId();
|
||||||
|
|
||||||
@ -991,9 +995,6 @@ NodeGraph::NodeGraph(Frame *f) : wxPanel(f, wxID_ANY) {
|
|||||||
} else if(ev.GetId() == idEncodeVp8) {
|
} else if(ev.GetId() == idEncodeVp8) {
|
||||||
noed = new GrNode(this);
|
noed = new GrNode(this);
|
||||||
noed->logical = CHi_EncodeVP8();
|
noed->logical = CHi_EncodeVP8();
|
||||||
} else if(ev.GetId() == idMuxWebm) {
|
|
||||||
noed = new GrNode(this);
|
|
||||||
noed->logical = CHi_MuxWebm();
|
|
||||||
} else if(ev.GetId() == idWindow) {
|
} else if(ev.GetId() == idWindow) {
|
||||||
noed = new GrNode(this);
|
noed = new GrNode(this);
|
||||||
noed->logical = CHi_Window();
|
noed->logical = CHi_Window();
|
||||||
@ -1033,6 +1034,9 @@ NodeGraph::NodeGraph(Frame *f) : wxPanel(f, wxID_ANY) {
|
|||||||
} else if(ev.GetId() == idEncodeAAC) {
|
} else if(ev.GetId() == idEncodeAAC) {
|
||||||
noed = new GrNode(this);
|
noed = new GrNode(this);
|
||||||
noed->logical = CHi_EncodeAAC();
|
noed->logical = CHi_EncodeAAC();
|
||||||
|
} else if(ev.GetId() == idMuxMatroska) {
|
||||||
|
noed = new GrNode(this);
|
||||||
|
noed->logical = CHi_MuxMatroska();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(noed) {
|
if(noed) {
|
||||||
|
Loading…
Reference in New Issue
Block a user