summaryrefslogtreecommitdiff
path: root/src/internal
diff options
context:
space:
mode:
authorTuomas Virtanen <katajakasa@gmail.com>2018-03-31 16:48:17 +0300
committerTuomas Virtanen <katajakasa@gmail.com>2018-03-31 16:48:17 +0300
commit77f1a8d1eb514b65c7f816933b4d11b4a0ce574b (patch)
treeaf1b053957c5a1571675efeeee7108dc1091610c /src/internal
parent591f359765045e68dc3e69742a63377b2b05525f (diff)
Add texture atlas for subtitles
Diffstat (limited to 'src/internal')
-rw-r--r--src/internal/kitdecoder.c18
-rw-r--r--src/internal/libass.c1
-rw-r--r--src/internal/subtitle/kitatlas.c231
-rw-r--r--src/internal/subtitle/kitsubtitle.c95
-rw-r--r--src/internal/subtitle/kitsubtitlepacket.c3
-rw-r--r--src/internal/subtitle/renderers/kitsubass.c132
-rw-r--r--src/internal/subtitle/renderers/kitsubimage.c38
-rw-r--r--src/internal/subtitle/renderers/kitsubrenderer.c11
-rw-r--r--src/internal/video/kitvideo.c2
9 files changed, 371 insertions, 160 deletions
diff --git a/src/internal/kitdecoder.c b/src/internal/kitdecoder.c
index 555c54f..4af927c 100644
--- a/src/internal/kitdecoder.c
+++ b/src/internal/kitdecoder.c
@@ -167,6 +167,16 @@ void* Kit_PeekDecoderOutput(Kit_Decoder *dec) {
return ret;
}
+void* Kit_ReadDecoderOutput(Kit_Decoder *dec) {
+ assert(dec != NULL);
+ void *ret = NULL;
+ if(SDL_LockMutex(dec->lock[KIT_DEC_OUT]) == 0) {
+ ret = Kit_ReadBuffer(dec->buffer[KIT_DEC_OUT]);
+ SDL_UnlockMutex(dec->lock[KIT_DEC_OUT]);
+ }
+ return ret;
+}
+
void Kit_ForEachDecoderOutput(Kit_Decoder *dec, Kit_ForEachItemCallback cb, void *userdata) {
assert(dec != NULL);
if(SDL_LockMutex(dec->lock[KIT_DEC_OUT]) == 0) {
@@ -190,6 +200,14 @@ void Kit_ClearDecoderBuffers(Kit_Decoder *dec) {
avcodec_flush_buffers(dec->codec_ctx);
}
+int Kit_LockDecoderOutput(Kit_Decoder *dec) {
+ return SDL_LockMutex(dec->lock[KIT_DEC_OUT]);
+}
+
+void Kit_UnlockDecoderOutput(Kit_Decoder *dec) {
+ SDL_UnlockMutex(dec->lock[KIT_DEC_OUT]);
+}
+
int Kit_RunDecoder(Kit_Decoder *dec) {
if(dec == NULL) return 0;
diff --git a/src/internal/libass.c b/src/internal/libass.c
index a13e32d..b8cba10 100644
--- a/src/internal/libass.c
+++ b/src/internal/libass.c
@@ -18,6 +18,7 @@ int load_libass(void *handle) {
ass_process_data = SDL_LoadFunction(handle, "ass_process_data");
ass_add_font = SDL_LoadFunction(handle, "ass_add_font");
ass_process_codec_private = SDL_LoadFunction(handle, "ass_process_codec_private");
+ ass_process_chunk = SDL_LoadFunction(handle, "ass_process_chunk");
return 0;
}
diff --git a/src/internal/subtitle/kitatlas.c b/src/internal/subtitle/kitatlas.c
new file mode 100644
index 0000000..4f98a6f
--- /dev/null
+++ b/src/internal/subtitle/kitatlas.c
@@ -0,0 +1,231 @@
+#include <assert.h>
+
+#include "kitchensink/internal/subtitle/kitatlas.h"
+#include "kitchensink/internal/utils/kitlog.h"
+
+static int min(int a, int b) {
+ if(a < b)
+ return a;
+ return b;
+}
+
+
+Kit_TextureAtlas* Kit_CreateAtlas(int w, int h) {
+ assert(w >= 1024);
+ assert(h >= 1024);
+
+ Kit_TextureAtlas *atlas = calloc(1, sizeof(Kit_TextureAtlas));
+ if(atlas == NULL) {
+ goto exit_0;
+ }
+ atlas->cur_items = 0;
+ atlas->max_items = 1024;
+ atlas->w = w;
+ atlas->h = h;
+
+ atlas->items = calloc(atlas->max_items, sizeof(Kit_TextureAtlasItem));
+ if(atlas->items == NULL) {
+ goto exit_1;
+ }
+
+ memset(atlas->shelf, 0, sizeof(atlas->shelf));
+ return atlas;
+
+exit_1:
+ free(atlas);
+exit_0:
+ return NULL;
+}
+
+void Kit_ResetAtlasContent(Kit_TextureAtlas *atlas) {
+ // Clear shelves & allocations on items
+ Kit_TextureAtlasItem *item;
+ memset(atlas->shelf, 0, sizeof(atlas->shelf));
+ for(int i = 0; i < atlas->cur_items; i++) {
+ item = &atlas->items[i];
+ item->cur_shelf = -1;
+ item->cur_slot = -1;
+ item->is_copied = false;
+ }
+}
+
+void Kit_ClearAtlasContent(Kit_TextureAtlas *atlas) {
+ Kit_TextureAtlasItem *item;
+ for(int i = 0; i < atlas->cur_items; i++) {
+ item = &atlas->items[i];
+ SDL_FreeSurface(item->surface);
+ }
+ atlas->cur_items = 0;
+ memset(atlas->shelf, 0, sizeof(atlas->shelf));
+}
+
+void Kit_FreeAtlas(Kit_TextureAtlas *atlas) {
+ assert(atlas != NULL);
+
+ Kit_ClearAtlasContent(atlas);
+ free(atlas->items);
+ free(atlas);
+}
+
+void Kit_SetItemAllocation(Kit_TextureAtlasItem *item, int shelf, int slot, int x, int y) {
+ item->cur_shelf = shelf;
+ item->cur_slot = slot;
+ item->source.x = x;
+ item->source.y = y;
+ item->source.w = item->surface->w;
+ item->source.h = item->surface->h;
+ //LOG("Writing x,y=(%d, %d) w,h=(%d, %d) on shelf %d slot %d\n",
+ // x, y, item->surface->w, item->surface->h, shelf, slot);
+}
+
+int Kit_FindFreeAtlasSlot(Kit_TextureAtlas *atlas, Kit_TextureAtlasItem *item) {
+ int shelf_w, shelf_h;
+ int total_remaining_h = atlas->h;
+ int total_reserved_h = 0;
+
+ // First, try to look for a good, existing shelf
+ int best_shelf_idx = -1;
+ int best_shelf_h = 0;
+
+ // Try to find a good shelf to put this item in
+ int shelf_idx;
+ for(shelf_idx = 0; shelf_idx < MAX_SHELVES; shelf_idx++) {
+ shelf_w = atlas->shelf[shelf_idx][0];
+ shelf_h = atlas->shelf[shelf_idx][1];
+ if(shelf_h == 0) {
+ break;
+ }
+ total_remaining_h -= shelf_h;
+ total_reserved_h += shelf_h;
+
+ // If the item fits, check if the space is better than previous one
+ if(item->surface->w <= (atlas->w - shelf_w) && item->surface->h <= shelf_h) {
+ if(shelf_h > best_shelf_h) {
+ best_shelf_h = shelf_h;
+ best_shelf_idx = shelf_idx;
+ }
+ }
+ }
+
+ // If existing shelf found, put the item there. Otherwise create a new shelf.
+ if(best_shelf_idx != -1) {
+ Kit_SetItemAllocation(
+ item,
+ best_shelf_idx,
+ atlas->shelf[best_shelf_idx][2],
+ atlas->shelf[best_shelf_idx][0],
+ total_reserved_h);
+ atlas->shelf[best_shelf_idx][0] += item->surface->w;
+ atlas->shelf[best_shelf_idx][2] += 1;
+ return 0;
+ } else if(total_remaining_h > item->surface->h) {
+ atlas->shelf[shelf_idx][0] = item->surface->w;
+ atlas->shelf[shelf_idx][1] = item->surface->h;
+ atlas->shelf[shelf_idx][2] = 1;
+ Kit_SetItemAllocation(
+ item,
+ shelf_idx,
+ 0,
+ 0,
+ total_reserved_h);
+ return 0;
+ }
+
+ return 1; // Can't fit!
+}
+
+int Kit_AllocateAtlasSurfaces(Kit_TextureAtlas *atlas, bool halt_on_errors) {
+ int ret = 0;
+ Kit_TextureAtlasItem *item;
+ for(int i = 0; i < atlas->cur_items; i++) {
+ item = &atlas->items[i];
+ if(item->cur_shelf > -1) // Stop if already allocated
+ continue;
+ if(Kit_FindFreeAtlasSlot(atlas, item) != 0) { // If nothing else helps, create a new slot
+ ret = 1;
+ if(halt_on_errors)
+ goto exit_0;
+ }
+ }
+
+exit_0:
+ return ret;
+}
+
+void Kit_BlitAtlasSurfaces(Kit_TextureAtlas *atlas, SDL_Texture *texture) {
+ Kit_TextureAtlasItem *item;
+ for(int i = 0; i < atlas->cur_items; i++) {
+ item = &atlas->items[i];
+ if(!item->is_copied) { // Copy if not yet copied
+ SDL_UpdateTexture(texture, &item->source, item->surface->pixels, item->surface->pitch);
+ item->is_copied = true;
+ }
+ }
+}
+
+int Kit_UpdateAtlasTexture(Kit_TextureAtlas *atlas, SDL_Texture *texture) {
+ assert(atlas != NULL);
+ assert(texture != NULL);
+
+ // Check if texture size has changed
+ int texture_w, texture_h;
+ if(SDL_QueryTexture(texture, NULL, NULL, &texture_w, &texture_h) == 0) {
+ if(texture_w != atlas->w || texture_h != atlas->h) {
+ Kit_ResetAtlasContent(atlas);
+ }
+ atlas->w = texture_w;
+ atlas->h = texture_h;
+ }
+
+ if(Kit_AllocateAtlasSurfaces(atlas, true) != 0) {
+ LOG("WARNING! FULL ATLAS, CANNOT MAKE MORE ROOM!\n");
+ }
+
+ // Blit unblitted surfaces to texture
+ Kit_BlitAtlasSurfaces(atlas, texture);
+ return 0;
+}
+
+int Kit_GetAtlasItems(const Kit_TextureAtlas *atlas, SDL_Rect *sources, SDL_Rect *targets, int limit) {
+ int count = 0;
+ for(int i = 0; i < min(atlas->cur_items, limit); i++) {
+ Kit_TextureAtlasItem *item = &atlas->items[i];
+
+ if(sources != NULL)
+ memcpy(&sources[count], &item->source, sizeof(SDL_Rect));
+ if(targets != NULL)
+ memcpy(&targets[count], &item->target, sizeof(SDL_Rect));
+
+ count++;
+ }
+ return count;
+}
+
+int Kit_AddAtlasItem(Kit_TextureAtlas *atlas, SDL_Surface *surface, SDL_Rect *target) {
+ assert(atlas != NULL);
+ assert(surface != NULL);
+ assert(target != NULL);
+
+ if(atlas->cur_items >= atlas->max_items) {
+ SDL_FreeSurface(surface);
+ return -1; // Cannot fit, buffer full!
+ }
+
+ Kit_TextureAtlasItem *item = &atlas->items[atlas->cur_items];
+ item->cur_shelf = -1;
+ item->cur_slot = -1;
+ item->is_copied = false;
+ item->surface = surface;
+ memset(&item->source, 0, sizeof(SDL_Rect));
+ memcpy(&item->target, target, sizeof(SDL_Rect));
+ return atlas->cur_items++;
+}
+
+void Kit_FreeAtlasItem(Kit_TextureAtlas *atlas, int item_id) {
+ assert(item_id >= 0);
+ SDL_FreeSurface(atlas->items[item_id].surface);
+ memmove(&atlas->items[item_id],
+ &atlas->items[item_id+1],
+ (atlas->max_items - item_id - 1) * sizeof(Kit_TextureAtlasItem));
+ atlas->cur_items--;
+}
diff --git a/src/internal/subtitle/kitsubtitle.c b/src/internal/subtitle/kitsubtitle.c
index 38d4768..3185bcd 100644
--- a/src/internal/subtitle/kitsubtitle.c
+++ b/src/internal/subtitle/kitsubtitle.c
@@ -11,13 +11,14 @@
#include "kitchensink/internal/kitlibstate.h"
#include "kitchensink/internal/subtitle/kitsubtitlepacket.h"
#include "kitchensink/internal/subtitle/kitsubtitle.h"
+#include "kitchensink/internal/subtitle/kitatlas.h"
#include "kitchensink/internal/subtitle/renderers/kitsubimage.h"
#include "kitchensink/internal/subtitle/renderers/kitsubass.h"
#include "kitchensink/internal/subtitle/renderers/kitsubrenderer.h"
#include "kitchensink/internal/utils/kithelpers.h"
-#define KIT_SUBTITLE_OUT_SIZE 1024
+#define KIT_SUBTITLE_OUT_SIZE 64
typedef struct Kit_SubtitleDecoder {
Kit_SubtitleFormat *format;
@@ -25,8 +26,7 @@ typedef struct Kit_SubtitleDecoder {
AVSubtitle scratch_frame;
int w;
int h;
- int output_state;
- SDL_Surface *tmp_buffer;
+ Kit_TextureAtlas *atlas;
} Kit_SubtitleDecoder;
@@ -82,7 +82,7 @@ static int dec_decode_subtitle_cb(Kit_Decoder *dec, AVPacket *in_packet) {
static void dec_close_subtitle_cb(Kit_Decoder *dec) {
if(dec == NULL) return;
Kit_SubtitleDecoder *subtitle_dec = dec->userdata;
- SDL_FreeSurface(subtitle_dec->tmp_buffer);
+ Kit_FreeAtlas(subtitle_dec->atlas);
Kit_CloseSubtitleRenderer(subtitle_dec->renderer);
free(subtitle_dec);
}
@@ -146,21 +146,19 @@ Kit_Decoder* Kit_CreateSubtitleDecoder(const Kit_Source *src, Kit_SubtitleFormat
goto exit_2;
}
- // Allocate temporary screen-sized subtitle buffer
- SDL_Surface *tmp_buffer = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_RGBA32);
- if(tmp_buffer == NULL) {
- Kit_SetError("Unable to allocate subtitle buffer");
+ // Allocate texture atlas for subtitle rectangles
+ Kit_TextureAtlas *atlas = Kit_CreateAtlas(1024, 1024);
+ if(atlas == NULL) {
+ Kit_SetError("Unable to allocate subtitle texture atlas");
goto exit_3;
}
- SDL_FillRect(tmp_buffer, NULL, 0);
// Set callbacks and userdata, and we're go
subtitle_dec->format = format;
subtitle_dec->renderer = ren;
subtitle_dec->w = w;
subtitle_dec->h = h;
- subtitle_dec->tmp_buffer = tmp_buffer;
- subtitle_dec->output_state = 0;
+ subtitle_dec->atlas = atlas;
dec->dec_decode = dec_decode_subtitle_cb;
dec->dec_close = dec_close_subtitle_cb;
dec->userdata = subtitle_dec;
@@ -176,79 +174,26 @@ exit_0:
return NULL;
}
-
-typedef struct {
- double sync_ts;
- SDL_Surface *surface;
- int rendered;
-} tmp_sub_image;
-
-
-static void _merge_subtitle_texture(void *ptr, void *userdata) {
- tmp_sub_image *img = userdata;
- Kit_SubtitlePacket *packet = ptr;
-
- // Make sure current time is within presentation range
- if(packet->pts_start >= img->sync_ts)
- return;
- if(packet->pts_end <= img->sync_ts && packet->pts_end >= 0)
- return;
-
- // Tell the renderer function that we did something here
- img->rendered = 1;
-
- // Blit source whole source surface to target surface in requested coords
- SDL_Rect dst_rect;
- dst_rect.x = packet->x;
- dst_rect.y = packet->y;
- SDL_BlitSurface(packet->surface, NULL, img->surface, &dst_rect);
-}
-
-
-int Kit_GetSubtitleDecoderDataTexture(Kit_Decoder *dec, SDL_Texture *texture) {
+int Kit_GetSubtitleDecoderData(Kit_Decoder *dec, SDL_Texture *texture, SDL_Rect *sources, SDL_Rect *targets, int limit) {
assert(dec != NULL);
assert(texture != NULL);
Kit_SubtitleDecoder *subtitle_dec = dec->userdata;
-
double sync_ts = _GetSystemTime() - dec->clock_sync;
- // If we rendered on last frame, clear the buffer
- if(subtitle_dec->output_state == 1) {
- SDL_FillRect(subtitle_dec->tmp_buffer, NULL, 0);
- }
+ // Tell the renderer to render content to atlas
+ Kit_GetSubtitleRendererData(subtitle_dec->renderer, subtitle_dec->atlas, sync_ts);
- // Clear out old packets
- Kit_SubtitlePacket *packet = NULL;
- while((packet = Kit_PeekDecoderOutput(dec)) != NULL) {
- if(packet->pts_end >= sync_ts)
- break;
- if(packet->pts_end < 0)
- break;
- Kit_AdvanceDecoderOutput(dec);
- free_out_subtitle_packet_cb(packet);
+ // Next, update the actual atlas texture contents. This may force the atlas to do partial or complete reordering
+ // OR it may simply be a texture update from cache.
+ if(Kit_UpdateAtlasTexture(subtitle_dec->atlas, texture) != 0) {
+ return -1;
}
- // Blit all subtitle image rectangles to master buffer
- tmp_sub_image img;
- img.sync_ts = sync_ts;
- img.surface = subtitle_dec->tmp_buffer;
- img.rendered = 0;
- Kit_ForEachDecoderOutput(dec, _merge_subtitle_texture, (void*)&img);
+ // Next, get targets and sources of visible atlas items
+ int item_count = Kit_GetAtlasItems(subtitle_dec->atlas, sources, targets, limit);
- // If nothing was rendered now or last frame, just return. No need to update texture.
+ // All done
dec->clock_pos = sync_ts;
- if(img.rendered == 0 && subtitle_dec->output_state == 0) {
- return 1;
- }
- subtitle_dec->output_state = img.rendered;
-
- // Update output texture with current buffered image
- SDL_UpdateTexture(
- texture, NULL,
- subtitle_dec->tmp_buffer->pixels,
- subtitle_dec->tmp_buffer->pitch);
-
- // all done!
- return 0;
+ return item_count;
}
diff --git a/src/internal/subtitle/kitsubtitlepacket.c b/src/internal/subtitle/kitsubtitlepacket.c
index ff52d39..78ab90e 100644
--- a/src/internal/subtitle/kitsubtitlepacket.c
+++ b/src/internal/subtitle/kitsubtitlepacket.c
@@ -2,9 +2,10 @@
Kit_SubtitlePacket* Kit_CreateSubtitlePacket(
- double pts_start, double pts_end, int pos_x, int pos_y, SDL_Surface *surface)
+ Kit_SubtitlePacketType type, double pts_start, double pts_end, int pos_x, int pos_y, SDL_Surface *surface)
{
Kit_SubtitlePacket *p = calloc(1, sizeof(Kit_SubtitlePacket));
+ p->type = type;
p->pts_start = pts_start;
p->pts_end = pts_end;
p->x = pos_x;
diff --git a/src/internal/subtitle/renderers/kitsubass.c b/src/internal/subtitle/renderers/kitsubass.c
index 2a9eedf..f59db07 100644
--- a/src/internal/subtitle/renderers/kitsubass.c
+++ b/src/internal/subtitle/renderers/kitsubass.c
@@ -7,49 +7,30 @@
#include "kitchensink/internal/utils/kitlog.h"
#include "kitchensink/internal/kitlibstate.h"
#include "kitchensink/internal/subtitle/kitsubtitlepacket.h"
+#include "kitchensink/internal/subtitle/kitatlas.h"
#include "kitchensink/internal/utils/kithelpers.h"
#include "kitchensink/internal/subtitle/renderers/kitsubass.h"
-// For compatibility
-#ifndef ASS_FONTPROVIDER_AUTODETECT
-#define ASS_FONTPROVIDER_AUTODETECT 1
-#endif
-
typedef struct Kit_ASSSubtitleRenderer {
ASS_Renderer *renderer;
ASS_Track *track;
} Kit_ASSSubtitleRenderer;
-static void _ProcessAssImage(SDL_Surface *surface, const ASS_Image *img, int min_x, int min_y) {
+static void Kit_ProcessAssImage(SDL_Surface *surface, const ASS_Image *img) {
unsigned char r = ((img->color) >> 24) & 0xFF;
unsigned char g = ((img->color) >> 16) & 0xFF;
unsigned char b = ((img->color) >> 8) & 0xFF;
unsigned char a = (img->color) & 0xFF;
unsigned char *src = img->bitmap;
unsigned char *dst = surface->pixels;
- unsigned int pos_x = img->dst_x - min_x;
- unsigned int pos_y = img->dst_y - min_y;
- unsigned int an, ao, x, y, x_off;
- dst += pos_y * surface->pitch;
+ unsigned int x, y;
for(y = 0; y < img->h; y++) {
for(x = 0; x < img->w; x++) {
- x_off = (pos_x + x) * 4;
- an = ((255 - a) * src[x]) >> 8; // New alpha
- ao = dst[x_off + 3]; // Original alpha
- if(ao == 0) {
- dst[x_off + 0] = r;
- dst[x_off + 1] = g;
- dst[x_off + 2] = b;
- dst[x_off + 3] = an;
- } else {
- dst[x_off + 3] = 255 - (255 - dst[x_off + 3]) * (255 - an) / 255;
- if(dst[x_off + 3] != 0) {
- dst[x_off + 0] = (dst[x_off + 0] * ao * (255-an) / 255 + r * an ) / dst[x_off + 3];
- dst[x_off + 1] = (dst[x_off + 1] * ao * (255-an) / 255 + g * an ) / dst[x_off + 3];
- dst[x_off + 2] = (dst[x_off + 2] * ao * (255-an) / 255 + b * an ) / dst[x_off + 3];
- }
- }
+ dst[x * 4 + 0] = r;
+ dst[x * 4 + 1] = g;
+ dst[x * 4 + 2] = b;
+ dst[x * 4 + 3] = ((255 - a) * src[x]) >> 8;
}
src += img->stride;
dst += surface->pitch;
@@ -62,67 +43,67 @@ static Kit_SubtitlePacket* ren_render_ass_cb(Kit_SubtitleRenderer *ren, void *sr
Kit_ASSSubtitleRenderer *ass_ren = ren->userdata;
AVSubtitle *sub = src;
- SDL_Surface *surface = NULL;
- ASS_Image *image = NULL;
- ASS_Image *wt_image = NULL;
- unsigned int now = start_pts * 1000;
- int change = 0;
- int x0 = INT_MAX, y0 = INT_MAX;
- int x1 = 0, y1 = 0;
- int w, h;
// Read incoming subtitle packets to libASS
- for(int r = 0; r < sub->num_rects; r++) {
- if(sub->rects[r]->ass == NULL)
- continue;
- ass_process_data(ass_ren->track, sub->rects[r]->ass, strlen(sub->rects[r]->ass));
- }
-
- // Ask libass to render frames. If there are no changes since last render, stop here.
- wt_image = image = ass_render_frame(ass_ren->renderer, ass_ren->track, now, &change);
- if(change == 0) {
- return NULL;
- }
-
- // Find dimensions
- for(image = wt_image; image; image = image->next) {
- if(image->dst_x < x0)
- x0 = image->dst_x;
- if(image->dst_y < y0)
- y0 = image->dst_y;
- if(image->dst_x + image->w > x1)
- x1 = image->dst_x + image->w;
- if(image->dst_y + image->h > y1)
- y1 = image->dst_y + image->h;
- }
- w = x1 - x0;
- h = y1 - y0;
-
- // Surface to render on
- surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_RGBA32);
- SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
- SDL_FillRect(surface, NULL, 0);
-
- // Render subimages to the target surface.
- for(image = wt_image; image; image = image->next) {
- if(image->w == 0 || image->h == 0)
- continue;
- _ProcessAssImage(surface, image, x0, y0);
+ if(Kit_LockDecoderOutput(ren->dec) == 0) {
+ for(int r = 0; r < sub->num_rects; r++) {
+ if(sub->rects[r]->ass == NULL)
+ continue;
+ ass_process_data(ass_ren->track, sub->rects[r]->ass, strlen(sub->rects[r]->ass));
+ }
+ Kit_UnlockDecoderOutput(ren->dec);
}
- // We tell subtitle handler to clear output before adding this frame.
- return Kit_CreateSubtitlePacket(start_pts, end_pts, x0, y0, surface);
+ return NULL;
}
static void ren_close_ass_cb(Kit_SubtitleRenderer *ren) {
if(ren == NULL) return;
Kit_ASSSubtitleRenderer *ass_ren = ren->userdata;
+ ass_free_track(ass_ren->track);
ass_renderer_done(ass_ren->renderer);
free(ass_ren);
}
-Kit_SubtitleRenderer* Kit_CreateASSSubtitleRenderer(const Kit_Decoder *dec, int w, int h) {
+static int ren_get_data_cb(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, double current_pts) {
+ Kit_ASSSubtitleRenderer *ass_ren = ren->userdata;
+ SDL_Surface *surface;
+ ASS_Image *image = NULL;
+ int change = 0;
+ unsigned int now = current_pts * 1000;
+
+ // First, we tell ASS to render images for us
+ if(Kit_LockDecoderOutput(ren->dec) == 0) {
+ image = ass_render_frame(ass_ren->renderer, ass_ren->track, now, &change);
+ Kit_UnlockDecoderOutput(ren->dec);
+ }
+
+ // If there was no change, stop here
+ if(change == 0) {
+ return 0;
+ }
+
+ // There was some change, process images and add them to atlas
+ Kit_ClearAtlasContent(atlas);
+ for(; image; image = image->next) {
+ if(image->w == 0 || image->h == 0)
+ continue;
+ surface = SDL_CreateRGBSurfaceWithFormat(0, image->w, image->h, 32, SDL_PIXELFORMAT_RGBA32);
+ SDL_FillRect(surface, NULL, 0);
+ Kit_ProcessAssImage(surface, image);
+ SDL_Rect target;
+ target.x = image->dst_x;
+ target.y = image->dst_y;
+ target.w = image->w;
+ target.h = image->h;
+ Kit_AddAtlasItem(atlas, surface, &target);
+ }
+
+ return 0;
+}
+
+Kit_SubtitleRenderer* Kit_CreateASSSubtitleRenderer(Kit_Decoder *dec, int w, int h) {
assert(dec != NULL);
assert(w >= 0);
assert(h >= 0);
@@ -135,7 +116,7 @@ Kit_SubtitleRenderer* Kit_CreateASSSubtitleRenderer(const Kit_Decoder *dec, int
}
// First allocate the generic decoder component
- Kit_SubtitleRenderer *ren = Kit_CreateSubtitleRenderer();
+ Kit_SubtitleRenderer *ren = Kit_CreateSubtitleRenderer(dec);
if(ren == NULL) {
goto exit_0;
}
@@ -196,13 +177,12 @@ Kit_SubtitleRenderer* Kit_CreateASSSubtitleRenderer(const Kit_Decoder *dec, int
dec->codec_ctx->subtitle_header_size);
}
- LOG("kekekekee\n");
-
// Set callbacks and userdata, and we're go
ass_ren->renderer = ass_renderer;
ass_ren->track = ass_track;
ren->ren_render = ren_render_ass_cb;
ren->ren_close = ren_close_ass_cb;
+ ren->ren_get_data = ren_get_data_cb;
ren->userdata = ass_ren;
return ren;
diff --git a/src/internal/subtitle/renderers/kitsubimage.c b/src/internal/subtitle/renderers/kitsubimage.c
index 4cd5b52..e39aae7 100644
--- a/src/internal/subtitle/renderers/kitsubimage.c
+++ b/src/internal/subtitle/renderers/kitsubimage.c
@@ -5,6 +5,7 @@
#include "kitchensink/kiterror.h"
#include "kitchensink/internal/utils/kitlog.h"
+#include "kitchensink/internal/subtitle/kitatlas.h"
#include "kitchensink/internal/subtitle/kitsubtitlepacket.h"
#include "kitchensink/internal/subtitle/renderers/kitsubimage.h"
@@ -19,6 +20,7 @@ static void _ProcessSubImage(SDL_Surface *surface, const AVSubtitleRect *rect, i
dst_rect.y = rect->y - min_y;
SDL_BlitSurface(src, NULL, surface, &dst_rect);
+ SDL_FreeSurface(src);
}
static Kit_SubtitlePacket* ren_render_image_cb(Kit_SubtitleRenderer *ren, void *src, double start_pts, double end_pts) {
@@ -28,7 +30,7 @@ static Kit_SubtitlePacket* ren_render_image_cb(Kit_SubtitleRenderer *ren, void *
AVSubtitle *sub = src;
SDL_Surface *surface = NULL;
int x0 = INT_MAX, y0 = INT_MAX;
- int x1 = 0, y1 = 0;
+ int x1 = INT_MIN, y1 = INT_MIN;
int w, h;
int has_content = 0;
@@ -54,7 +56,6 @@ static Kit_SubtitlePacket* ren_render_image_cb(Kit_SubtitleRenderer *ren, void *
w = x1 - x0;
h = y1 - y0;
- LOG("x, y = %d, %d w, h = %d, %d\n", x0, y0, w, h);
// Surface to render on
surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_RGBA32);
@@ -69,24 +70,49 @@ static Kit_SubtitlePacket* ren_render_image_cb(Kit_SubtitleRenderer *ren, void *
_ProcessSubImage(surface, r, x0, y0);
}
- LOG("Setting %f, %f\n", start_pts, end_pts);
- return Kit_CreateSubtitlePacket(start_pts, end_pts, x0, y0, surface);
+ SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE);
+ return Kit_CreateSubtitlePacket(KIT_SUBTITLE_PACKET_ADD, start_pts, end_pts, x0, y0, surface);
}
+static int ren_get_data_cb(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, double current_pts) {
+ // Read any new packets to atlas
+ Kit_SubtitlePacket *packet = NULL;
+ while((packet = Kit_PeekDecoderOutput(ren->dec)) != NULL) {
+ if(packet->pts_end < current_pts) {
+ Kit_AdvanceDecoderOutput(ren->dec);
+ Kit_FreeSubtitlePacket(packet);
+ }
+ if(packet->pts_start > current_pts) {
+ break;
+ }
+
+ SDL_Rect target;
+ target.x = packet->x;
+ target.y = packet->y;
+ target.w = packet->surface->w;
+ target.h = packet->surface->h;
+ // NOTE! Surfaces ARE NOT COPIED! WE DIRECTLY HAND OVER THE POINTER TO EXISTING DATA! This is why we cannot
+ // free the surface after adding it to atlas.
+ Kit_AddAtlasItem(atlas, packet->surface, &target);
+ packet->surface = NULL;
+ }
+ return 0;
+}
-Kit_SubtitleRenderer* Kit_CreateImageSubtitleRenderer(const Kit_Decoder *dec, int w, int h) {
+Kit_SubtitleRenderer* Kit_CreateImageSubtitleRenderer(Kit_Decoder *dec, int w, int h) {
assert(dec != NULL);
assert(w >= 0);
assert(h >= 0);
// Allocate a new renderer
- Kit_SubtitleRenderer *ren = Kit_CreateSubtitleRenderer();
+ Kit_SubtitleRenderer *ren = Kit_CreateSubtitleRenderer(dec);
if(ren == NULL) {
return NULL;
}
// Only renderer required, no other data.
ren->ren_render = ren_render_image_cb;
+ ren->ren_get_data = ren_get_data_cb;
ren->ren_close = NULL;
ren->userdata = NULL;
return ren;
diff --git a/src/internal/subtitle/renderers/kitsubrenderer.c b/src/internal/subtitle/renderers/kitsubrenderer.c
index ce3e408..0e628bc 100644
--- a/src/internal/subtitle/renderers/kitsubrenderer.c
+++ b/src/internal/subtitle/renderers/kitsubrenderer.c
@@ -6,13 +6,14 @@
#include "kitchensink/internal/subtitle/renderers/kitsubrenderer.h"
-Kit_SubtitleRenderer* Kit_CreateSubtitleRenderer() {
+Kit_SubtitleRenderer* Kit_CreateSubtitleRenderer(Kit_Decoder *dec) {
// Allocate renderer and make sure allocation was a success
Kit_SubtitleRenderer *ren = calloc(1, sizeof(Kit_SubtitleRenderer));
if(ren == NULL) {
Kit_SetError("Unable to allocate kit subtitle renderer");
return NULL;
}
+ ren->dec = dec;
return ren;
}
@@ -24,6 +25,14 @@ Kit_SubtitlePacket* Kit_RunSubtitleRenderer(Kit_SubtitleRenderer *ren, void *src
return NULL;
}
+int Kit_GetSubtitleRendererData(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, double current_pts) {
+ if(ren == NULL)
+ return 0;
+ if(ren->ren_get_data != NULL)
+ return ren->ren_get_data(ren, atlas, current_pts);
+ return 0;
+}
+
void Kit_CloseSubtitleRenderer(Kit_SubtitleRenderer *ren) {
if(ren == NULL)
return;
diff --git a/src/internal/video/kitvideo.c b/src/internal/video/kitvideo.c
index 631ff35..ff3d05c 100644
--- a/src/internal/video/kitvideo.c
+++ b/src/internal/video/kitvideo.c
@@ -203,7 +203,7 @@ exit_0:
return NULL;
}
-int Kit_GetVideoDecoderDataTexture(Kit_Decoder *dec, SDL_Texture *texture) {
+int Kit_GetVideoDecoderData(Kit_Decoder *dec, SDL_Texture *texture) {
assert(dec != NULL);
assert(texture != NULL);