Make iron_mp4 work

This commit is contained in:
luboslenco
2024-12-02 13:50:56 +01:00
parent a0db3951d1
commit 7177d8911e
2 changed files with 131 additions and 10 deletions
+2
View File
@@ -384,6 +384,8 @@ if (flags.with_image_write) {
if (flags.with_video_write) {
project.add_define("WITH_VIDEO_WRITE");
project.add_cfiles("sources/libs/minimp4.c");
project.add_cfiles("sources/libs/minih264e.c");
}
project.flatten();
+129 -10
View File
@@ -395,7 +395,8 @@ unsigned char *iron_deflate_raw(unsigned char *data, int data_len, int *out_len,
#include <stb_image_write.h>
#endif
#ifdef WITH_VIDEO_WRITE
#include <jo_mpeg.h>
#include <minih264e.h>
#include <minimp4.h>
#endif
#ifdef WITH_COMPRESS
#define SDEFL_IMPLEMENTATION
@@ -2699,21 +2700,139 @@ buffer_t *iron_encode_png(buffer_t *bytes, i32 w, i32 h, i32 format) {
#endif
#ifdef WITH_VIDEO_WRITE
static FILE *iron_mpeg_fp;
void iron_mpeg_begin(char *path) {
iron_mpeg_fp = fopen(path, "wb");
static FILE *iron_mp4_fp;
static int iron_mp4_w;
static int iron_mp4_h;
static int iron_mp4_stride;
static H264E_persist_t *iron_mp4_enc = NULL;
static H264E_scratch_t *iron_mp4_scratch = NULL;
static char iron_mp4_path[512];
static char iron_mp4_path_264[512];
static char *iron_mp4_yuv_buf;
static size_t iron_mp4_get_nal_size(uint8_t *buf, size_t size) {
size_t pos = 3;
while ((size - pos) > 3) {
if (buf[pos] == 0 && buf[pos + 1] == 0 && buf[pos + 2] == 1) {
return pos;
}
if (buf[pos] == 0 && buf[pos + 1] == 0 && buf[pos + 2] == 0 && buf[pos + 3] == 1) {
return pos;
}
pos++;
}
return size;
}
void iron_mpeg_end() {
if (iron_mpeg_fp != NULL) {
fclose(iron_mpeg_fp);
iron_mpeg_fp = NULL;
static int iron_mp4_write_callback(int64_t offset, const void *buffer, size_t size, void *token) {
FILE *f = (FILE *)token;
fseek(f, offset, SEEK_SET);
return fwrite(buffer, 1, size, f) != size;
}
void iron_mp4_begin(char *path, i32 w, i32 h) {
strcpy(iron_mp4_path, path);
strcpy(iron_mp4_path_264, path);
int len = strlen(iron_mp4_path_264);
iron_mp4_path_264[len - 1] = '4';
iron_mp4_path_264[len - 2] = '6';
iron_mp4_path_264[len - 3] = '2';
iron_mp4_stride = w;
iron_mp4_w = w - w % 16;
iron_mp4_h = h - h % 16;
H264E_create_param_t create_param = {0};
create_param.width = iron_mp4_w;
create_param.height = iron_mp4_h;
int sizeof_persist = 0;
int sizeof_scratch = 0;
H264E_sizeof(&create_param, &sizeof_persist, &sizeof_scratch);
iron_mp4_enc = (H264E_persist_t *)malloc(sizeof_persist);
iron_mp4_scratch = (H264E_scratch_t *)malloc(sizeof_scratch);
H264E_init(iron_mp4_enc, &create_param);
iron_mp4_fp = fopen(iron_mp4_path_264, "wb");
int frame_size = (int)(iron_mp4_w * iron_mp4_h * 1.5);
iron_mp4_yuv_buf = malloc(frame_size);
}
void iron_mp4_end() {
if (iron_mp4_fp == NULL) {
return;
}
buffer_t *blob = iron_load_blob(iron_mp4_path_264);
uint8_t *buf = blob->buffer;
size_t buf_size = blob->length;
FILE *fout = fopen(iron_mp4_path, "wb");
MP4E_mux_t *mux = MP4E_open(0, 0, fout, iron_mp4_write_callback);
mp4_h26x_writer_t mp4wr;
mp4_h26x_write_init(&mp4wr, mux, iron_mp4_w, iron_mp4_h, false);
while (buf_size > 0) {
size_t nal_size = iron_mp4_get_nal_size(buf, buf_size);
if (nal_size < 4) {
buf += 1;
buf_size -= 1;
continue;
}
int fps = 30;
mp4_h26x_write_nal(&mp4wr, buf, nal_size, 90000 / fps);
buf += nal_size;
buf_size -= nal_size;
}
MP4E_close(mux);
mp4_h26x_write_close(&mp4wr);
free(iron_mp4_enc);
free(iron_mp4_scratch);
free(iron_mp4_yuv_buf);
fclose(iron_mp4_fp);
iron_mp4_fp = NULL;
}
void iron_mpeg_write(buffer_t *bytes, i32 w, i32 h) {
jo_write_mpeg(iron_mpeg_fp, bytes->buffer, w, h, 60);
void iron_mp4_encode(buffer_t *pixels) {
// rgba to yuv420p
for (int i = 0; i < iron_mp4_w; ++i) {
for (int j = 0; j < iron_mp4_h; ++j) {
int k = i + j * iron_mp4_stride;
uint8_t r = pixels->buffer[k * 4];
uint8_t g = pixels->buffer[k * 4 + 1];
uint8_t b = pixels->buffer[k * 4 + 2];
uint8_t y = (( 66 * r + 129 * g + 25 * b + 128) / 256) + 16;
uint8_t u = ((-38 * r - 74 * g + 112 * b + 128) / 256) + 128;
uint8_t v = ((112 * r - 94 * g - 18 * b + 128) / 256) + 128;
int l = i + j * iron_mp4_w;
int m = i / 2 + j / 2 * (iron_mp4_w / 2);
iron_mp4_yuv_buf[l] = y;
iron_mp4_yuv_buf[iron_mp4_w * iron_mp4_h + m] = u;
iron_mp4_yuv_buf[iron_mp4_w * iron_mp4_h + (iron_mp4_w * iron_mp4_h) / 4 + m] = v;
}
}
H264E_run_param_t run_param = {0};
run_param.frame_type = 0;
run_param.encode_speed = H264E_SPEED_SLOWEST; // H264E_SPEED_FASTEST;
run_param.desired_frame_bytes = 2048 * 1000 / 8 / 30; // 2048 kbps
run_param.qp_min = 10;
run_param.qp_max = 50;
H264E_io_yuv_t yuv;
yuv.yuv[0] = iron_mp4_yuv_buf;
yuv.stride[0] = iron_mp4_w;
yuv.yuv[1] = iron_mp4_yuv_buf + iron_mp4_w * iron_mp4_h;
yuv.stride[1] = iron_mp4_w / 2;
yuv.yuv[2] = iron_mp4_yuv_buf + (int)(iron_mp4_w * iron_mp4_h * 1.25);
yuv.stride[2] = iron_mp4_w / 2;
uint8_t *coded_data;
int sizeof_coded_data;
H264E_encode(iron_mp4_enc, iron_mp4_scratch, &run_param, &yuv, &coded_data, &sizeof_coded_data);
fwrite(coded_data, sizeof_coded_data, 1, iron_mp4_fp);
}
#endif