diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | ytdlsb-main.c | 207 |
2 files changed, 147 insertions, 62 deletions
@@ -1,4 +1,4 @@ -LIBPKGS=libcjson libcurl libwebp +LIBPKGS=libcjson libcurl libwebp libjpeg CFLAGS=$(shell pkg-config --cflags mpv) \ $(shell pkg-config --cflags $(LIBPKGS)) \ -pthread \ 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; } |
