summaryrefslogtreecommitdiff
path: root/ytdlsb-main.c
diff options
context:
space:
mode:
authordyknon dyknonr5fjp2026-01-18 06:07:19 +0900
committerdyknon dyknonr5fjp2026-01-18 06:07:19 +0900
commit57ea866cf32e9558aa58f536398c551965c3a4b8 (patch)
treeb6b839d0e822b9948de084483797f24f6054b00c /ytdlsb-main.c
parent7f3c4d52fead36d78c36fd99e2c8b92d0838f2db (diff)
Add jpeg decoding code. It works on twitch now.
Diffstat (limited to 'ytdlsb-main.c')
-rw-r--r--ytdlsb-main.c207
1 files changed, 146 insertions, 61 deletions
diff --git a/ytdlsb-main.c b/ytdlsb-main.c
index 7b0c0f8..b939369 100644
--- a/ytdlsb-main.c
+++ b/ytdlsb-main.c
@@ -3,12 +3,14 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
+#include <setjmp.h>
#include "ytdlsb-utils.h"
#include "ytdlsb-tasks.h"
#include "ytdlsb-mpv.h"
#include <cJSON.h>
#include <curl/curl.h>
#include <webp/decode.h>
+#include <jpeglib.h>
enum ytdlsb_hook{
YTDLSB_HOOK_PRELOADED,
@@ -383,80 +385,163 @@ void ytdlsb_sb_dump(struct ytdlsb_sb *sb){
}
}
+int ytdlsb_sb_fragment_raw_alloc(
+ struct ytdlsb_sb *sb, struct ytdlsb_sb_fragment *f,
+ size_t width, size_t height
+){
+ CK(err, SIZE_MAX / width / height, >= 4);
+ CK(err, width, <= sb->columns * sb->width);
+ CK(err, width % sb->width, == 0);
+ CK(err, height, <= sb->rows * sb->height);
+ CK(err, height % sb->height, == 0);
+ f->data_len = width * height * 4;
+ free(f->data);
+ f->data = CKAR(malloc(f->data_len));
+ return 0;
+err:
+ return -1;
+}
+int ytdlsb_sb_fragment_raw_fill_scanline(
+ struct ytdlsb_sb *sb, struct ytdlsb_sb_fragment *f,
+ char *src, size_t width, size_t pos_y
+){
+ CK(err, width*(pos_y+1)*4, <= f->data_len);
+ size_t frame_cnt = width / sb->width;
+ size_t frame_from = (pos_y / sb->height) * frame_cnt;
+ size_t y_in_row = pos_y % sb->height;
+ size_t frame_bytes = sb->width*sb->height*4;
+ for(size_t fi = 0; fi < frame_cnt; fi++){
+ memcpy(f->data + frame_bytes*(frame_from+fi) + y_in_row*sb->width*4,
+ src + sb->width*fi*4,
+ sb->width*4);
+ }
+ return 0;
+err:
+ return -1;
+}
+struct ytdlsb_jpeg_error{
+ struct jpeg_error_mgr pub;
+ jmp_buf env;
+};
+void ytdlsb_jpeg_error_exit(j_common_ptr cinfo){
+ struct ytdlsb_jpeg_error *e = (struct ytdlsb_jpeg_error *)cinfo->err;
+ longjmp(e->env, 1);
+}
+int ytdlsb_sb_fragment_decode(
+ struct ytdlsb_sb *sb, struct ytdlsb_sb_fragment *f
+){
+ CKAR(f->data);
+ if(f->state == YTDLSB_SB_WEBP){
+ char *decbuf = NULL;
+ int width_i, height_i;
+ size_t width, height;
+ if(is_little_endian()){
+ decbuf = (char *)CKR(webp_err, WebPDecodeBGRA(
+ (uint8_t *)f->data, f->data_len, &width_i, &height_i));
+ }else{
+ decbuf = (char *)CKR(webp_err, WebPDecodeARGB(
+ (uint8_t *)f->data, f->data_len, &width_i, &height_i));
+ }
+ width = TRY_NUMCAST(webp_err, size_t, width_i);
+ height = TRY_NUMCAST(webp_err, size_t, height_i);
+ CKP(webp_err, ytdlsb_sb_fragment_raw_alloc(sb, f, width, height));
+ for(size_t i = 0; i < height; i++){
+ CKP(webp_err, ytdlsb_sb_fragment_raw_fill_scanline(
+ sb, f, decbuf + width*i*4, width, i));
+ }
+ goto webp_ok;
+webp_err:
+ free(decbuf);
+ goto err;
+webp_ok:
+ }else if(f->state == YTDLSB_SB_JPEG){
+ int progress = 0;
+ struct jpeg_decompress_struct jd;
+ struct ytdlsb_jpeg_error je;
+ void *src;
+ JSAMPLE *decbuf;
+ size_t width, height;
+ unsigned long data_len = TRY_NUMCAST(err, unsigned long, f->data_len);
+
+ if(setjmp(je.env)){
+ if(progress >= 3) free(decbuf);
+ if(progress >= 2) free(src);
+ if(progress >= 1) jpeg_destroy_decompress(&jd);
+ goto err;
+ }
+ jd.err = jpeg_std_error(&je.pub);
+ je.pub.error_exit = ytdlsb_jpeg_error_exit;
+
+ jpeg_create_decompress(&jd);
+ progress = 1;
+
+ src = f->data;
+ f->data = NULL;
+ progress = 2;
+
+ jpeg_mem_src(&jd, src, data_len);
+ CK(jpeg_err, jpeg_read_header(&jd, 1), == JPEG_HEADER_OK);
+ if(is_little_endian()){
+ jd.out_color_space = JCS_EXT_RGBA;
+ }else{
+ jd.out_color_space = JCS_EXT_ARGB;
+ }
+ jpeg_start_decompress(&jd);
+
+ CK(jpeg_err, jd.output_components, == 4);
+ width = TRY_NUMCAST(jpeg_err, size_t, jd.output_width);
+ height = TRY_NUMCAST(jpeg_err, size_t, jd.output_height);
+ CKP(jpeg_err, ytdlsb_sb_fragment_raw_alloc(sb, f, width, height));
+ CKA(sizeof(JSAMPLE), == 1);
+ decbuf = CKAR(malloc(width * 4));
+ progress = 3;
+
+ for(size_t i = 0; i < height; i++){
+ CK(jpeg_err, jpeg_read_scanlines(&jd, &decbuf, 1), == 1);
+ CKP(jpeg_err, ytdlsb_sb_fragment_raw_fill_scanline(
+ sb, f, (char *)decbuf, width, i));
+ }
+ jpeg_finish_decompress(&jd);
+
+ free(decbuf);
+ free(src);
+ jpeg_destroy_decompress(&jd);
+ goto jpeg_ok;
+jpeg_err:
+ longjmp(je.env, 1);
+jpeg_ok:
+ }
+ return 0;
+err:
+ return -1;
+}
+
// <0 = error (only on first error)
// 0 = not loaded
// 1 = ref
-// 2 = copy(?)
int ytdlsb_sb_get_frame(
struct ytdlsb_sb *sb, double pos, char **data, size_t *len
){
struct ytdlsb_sb_fragment *f = NULL;
+ size_t frame = sb->width * sb->height * 4;
+ size_t findex;
for(size_t i = 0; i < sb->fragment_num; i++){
struct ytdlsb_sb_fragment *cf = &sb->fragments[i];
if(cf->start <= pos) f = cf;
else break;
}
- //eprintf("fragment %d\n", (int)((f-sb->fragments)/sizeof(*f)));
- if(!f || f->state == YTDLSB_SB_EMPTY) return 0;
- //eprintf(".!!!\n");
- if(f->state == YTDLSB_SB_WEBP){
- //eprintf("webp!\n");
- char *decoded;
- int width, height;
- if(is_little_endian()){
- decoded = (char *)CKR(decode_failed, WebPDecodeBGRA(
- (uint8_t *)f->data, f->data_len, &width, &height));
- }else{
- decoded = (char *)CKR(decode_failed, WebPDecodeRGBA(
- (uint8_t *)f->data, f->data_len, &width, &height));
- }
- CK(sizemismatch, width, <= sb->columns * sb->width);
- CK(sizemismatch, width % sb->width, == 0);
- CK(sizemismatch, height, <= sb->rows * sb->height);
- CK(sizemismatch, height % sb->height, == 0);
- f->data_len = TRY_NUMCAST(sizemismatch, size_t, width)
- * TRY_NUMCAST(sizemismatch, size_t, height) * 4;
- free(f->data);
- f->data = CKAR(malloc(f->data_len));
- size_t frame = sb->width * sb->height * 4;
- for(size_t y = 0; y < height/sb->height; y++){
- for(size_t x = 0; x < width/sb->width; x++){
- char *src_orig = decoded + (x*sb->width + y*sb->height*width)*4;
- char *dest_orig = f->data + (x + y*(width/sb->width))*frame;
- for(size_t i = 0; i < sb->height; i++){
- assert(src_orig + i*width*4 + 4*sb->width
- <= decoded+f->data_len);
- assert(dest_orig + i*sb->width*4 + 4*sb->width
- <= f->data+f->data_len);
- memcpy(dest_orig + i*sb->width*4,
- src_orig + i*width*4, 4*sb->width);
- }
- }
- }
- free(decoded);
- f->state = YTDLSB_SB_RAW;
- goto webpok;
-sizemismatch:
- free(decoded);
- goto decode_failed;
-webpok:
- }else if(f->state == YTDLSB_SB_JPEG){
- eprintf("Jpeg decoding is not implemented yet.\n");
- goto decode_failed;
- }
- if(f->state == YTDLSB_SB_RAW){
- //eprintf("raw!\n");
- size_t frame = sb->width * sb->height * 4;
- size_t findex = (pos - f->start) * sb->fps;
- if(f->data_len < findex*frame+frame){
- findex = (f->data_len - frame) / frame;
- }
- *data = f->data + frame*findex;
- *len = frame;
- return 1;
+ if(!f || f->state == YTDLSB_SB_EMPTY || f->state == YTDLSB_SB_FAILED)
+ return 0;
+ CKP(err, ytdlsb_sb_fragment_decode(sb, f));
+ f->state = YTDLSB_SB_RAW;
+ findex = (pos - f->start) * sb->fps;
+ if(f->data_len < findex*frame+frame){
+ findex = (f->data_len - frame) / frame;
}
- return 0;
-decode_failed:
+ *data = f->data + frame*findex;
+ *len = frame;
+ return 1;
+err:
f->state = YTDLSB_SB_FAILED;
return -1;
}