summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTuomas Virtanen <katajakasa@gmail.com>2018-07-02 00:57:10 +0300
committerTuomas Virtanen <katajakasa@gmail.com>2018-07-02 00:57:10 +0300
commit2c7242e8f88d180876d1f693aa0ca998c6467766 (patch)
treed241de0ba1d6b4ab761abe8ac36315c7a5320400
parent406491cf0378a02d0188e2d3c92518d571db82f1 (diff)
Reduce memory usage of subtitles
-rw-r--r--include/kitchensink/internal/subtitle/kitatlas.h7
-rw-r--r--include/kitchensink/internal/subtitle/kitsubtitle.h5
-rw-r--r--include/kitchensink/internal/subtitle/renderers/kitsubrenderer.h6
-rw-r--r--src/internal/subtitle/kitatlas.c144
-rw-r--r--src/internal/subtitle/kitsubtitle.c19
-rw-r--r--src/internal/subtitle/renderers/kitsubass.c5
-rw-r--r--src/internal/subtitle/renderers/kitsubimage.c9
-rw-r--r--src/internal/subtitle/renderers/kitsubrenderer.c4
-rw-r--r--src/kitplayer.c10
9 files changed, 73 insertions, 136 deletions
diff --git a/include/kitchensink/internal/subtitle/kitatlas.h b/include/kitchensink/internal/subtitle/kitatlas.h
index c901259..d9207c1 100644
--- a/include/kitchensink/internal/subtitle/kitatlas.h
+++ b/include/kitchensink/internal/subtitle/kitatlas.h
@@ -10,10 +10,8 @@
typedef struct Kit_TextureAtlasItem {
int cur_shelf; //< Current shelf number in cache
int cur_slot; //< Current slot on shelf in cache
- bool is_copied; //< Is copied to cache surface
SDL_Rect source; //< Source coordinates on cache surface
SDL_Rect target; //< Target coordinates on output surface
- SDL_Surface *surface; //< Current item surface
} Kit_TextureAtlasItem;
typedef struct Kit_Shelf {
@@ -34,10 +32,9 @@ typedef struct Kit_TextureAtlas {
KIT_LOCAL Kit_TextureAtlas* Kit_CreateAtlas();
KIT_LOCAL void Kit_FreeAtlas(Kit_TextureAtlas *atlas);
-KIT_LOCAL void Kit_RefreshAtlas(Kit_TextureAtlas *atlas, double current_pts);
KIT_LOCAL void Kit_ClearAtlasContent(Kit_TextureAtlas *atlas);
-KIT_LOCAL int Kit_UpdateAtlasTexture(Kit_TextureAtlas *atlas, SDL_Texture *texture);
+KIT_LOCAL void Kit_CheckAtlasTextureSize(Kit_TextureAtlas *atlas, SDL_Texture *texture);
KIT_LOCAL int Kit_GetAtlasItems(const Kit_TextureAtlas *atlas, SDL_Rect *sources, SDL_Rect *targets, int limit);
-KIT_LOCAL int Kit_AddAtlasItem(Kit_TextureAtlas *atlas, SDL_Surface *surface, const SDL_Rect *target);
+KIT_LOCAL int Kit_AddAtlasItem(Kit_TextureAtlas *atlas, SDL_Texture *texture, SDL_Surface *surface, const SDL_Rect *target);
#endif // KITATLAS_H
diff --git a/include/kitchensink/internal/subtitle/kitsubtitle.h b/include/kitchensink/internal/subtitle/kitsubtitle.h
index 7ea6fbd..8bb2a62 100644
--- a/include/kitchensink/internal/subtitle/kitsubtitle.h
+++ b/include/kitchensink/internal/subtitle/kitsubtitle.h
@@ -9,8 +9,9 @@
KIT_LOCAL Kit_Decoder* Kit_CreateSubtitleDecoder(
const Kit_Source *src, int stream_index, int video_w, int video_h, int screen_w, int screen_h);
-KIT_LOCAL int Kit_GetSubtitleDecoderData(
- Kit_Decoder *dec, SDL_Texture *texture, SDL_Rect *sources, SDL_Rect *targets, int limit);
+KIT_LOCAL void Kit_GetSubtitleDecoderTexture(Kit_Decoder *dec, SDL_Texture *texture);
KIT_LOCAL void Kit_SetSubtitleDecoderSize(Kit_Decoder *dec, int w, int h);
+KIT_LOCAL int Kit_GetSubtitleDecoderInfo(
+ Kit_Decoder *dec, SDL_Texture *texture, SDL_Rect *sources, SDL_Rect *targets, int limit);
#endif // KITSUBTITLE_H
diff --git a/include/kitchensink/internal/subtitle/renderers/kitsubrenderer.h b/include/kitchensink/internal/subtitle/renderers/kitsubrenderer.h
index e07aa55..3c37b00 100644
--- a/include/kitchensink/internal/subtitle/renderers/kitsubrenderer.h
+++ b/include/kitchensink/internal/subtitle/renderers/kitsubrenderer.h
@@ -1,6 +1,8 @@
#ifndef KITSUBRENDERER_H
#define KITSUBRENDERER_H
+#include <SDL_render.h>
+
#include "kitchensink/kitsource.h"
typedef struct Kit_SubtitleRenderer Kit_SubtitleRenderer;
@@ -8,7 +10,7 @@ typedef struct Kit_TextureAtlas Kit_TextureAtlas;
typedef struct Kit_Decoder Kit_Decoder;
typedef void (*ren_render_cb)(Kit_SubtitleRenderer *ren, void *src, double start_pts, double end_pts);
-typedef int (*ren_get_data_cb)(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, double current_pts);
+typedef int (*ren_get_data_cb)(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, SDL_Texture *texture, double current_pts);
typedef void (*ren_set_size_cb)(Kit_SubtitleRenderer *ren, int w, int h);
typedef void (*ren_close_cb)(Kit_SubtitleRenderer *ren);
@@ -23,7 +25,7 @@ struct Kit_SubtitleRenderer {
KIT_LOCAL Kit_SubtitleRenderer* Kit_CreateSubtitleRenderer(Kit_Decoder *dec);
KIT_LOCAL void Kit_RunSubtitleRenderer(Kit_SubtitleRenderer *ren, void *src, double start_pts, double end_pts);
-KIT_LOCAL int Kit_GetSubtitleRendererData(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, double current_pts);
+KIT_LOCAL int Kit_GetSubtitleRendererData(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, SDL_Texture *texture, double current_pts);
KIT_LOCAL void Kit_SetSubtitleRendererSize(Kit_SubtitleRenderer *ren, int w, int h);
KIT_LOCAL void Kit_CloseSubtitleRenderer(Kit_SubtitleRenderer *ren);
diff --git a/src/internal/subtitle/kitatlas.c b/src/internal/subtitle/kitatlas.c
index 4f6e5bc..e7bb654 100644
--- a/src/internal/subtitle/kitatlas.c
+++ b/src/internal/subtitle/kitatlas.c
@@ -43,22 +43,7 @@ exit_0:
return NULL;
}
-void Kit_ResetAtlasContent(Kit_TextureAtlas *atlas) {
- // Clear shelves & allocations on items
- Kit_TextureAtlasItem *item;
- memset(atlas->shelves, 0, atlas->max_shelves * sizeof(Kit_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) {
- for(int i = 0; i < atlas->cur_items; i++) {
- SDL_FreeSurface(atlas->items[i].surface);
- }
atlas->cur_items = 0;
memset(atlas->items, 0, atlas->max_items * sizeof(Kit_TextureAtlasItem));
memset(atlas->shelves, 0, atlas->max_shelves * sizeof(Kit_Shelf));
@@ -66,26 +51,23 @@ void Kit_ClearAtlasContent(Kit_TextureAtlas *atlas) {
void Kit_FreeAtlas(Kit_TextureAtlas *atlas) {
assert(atlas != NULL);
- for(int i = 0; i < atlas->cur_items; i++) {
- SDL_FreeSurface(atlas->items[i].surface);
- }
free(atlas->items);
free(atlas->shelves);
free(atlas);
}
-void Kit_SetItemAllocation(Kit_TextureAtlasItem *item, int shelf, int slot, int x, int y) {
+void Kit_SetItemAllocation(Kit_TextureAtlasItem *item, SDL_Surface *surface, int shelf, int slot, int x, int y) {
assert(item != NULL);
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;
+ item->source.w = surface->w;
+ item->source.h = surface->h;
}
-int Kit_FindFreeAtlasSlot(Kit_TextureAtlas *atlas, Kit_TextureAtlasItem *item) {
+int Kit_FindFreeAtlasSlot(Kit_TextureAtlas *atlas, SDL_Surface *surface, Kit_TextureAtlasItem *item) {
assert(atlas != NULL);
assert(item != NULL);
@@ -111,12 +93,10 @@ int Kit_FindFreeAtlasSlot(Kit_TextureAtlas *atlas, Kit_TextureAtlasItem *item) {
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;
- best_shelf_y = total_reserved_h - shelf_h;
- }
+ if(surface->w <= (atlas->w - shelf_w) && surface->h <= shelf_h && shelf_h < best_shelf_h) {
+ best_shelf_h = shelf_h;
+ best_shelf_idx = shelf_idx;
+ best_shelf_y = total_reserved_h - shelf_h;
}
}
@@ -124,19 +104,21 @@ int Kit_FindFreeAtlasSlot(Kit_TextureAtlas *atlas, Kit_TextureAtlasItem *item) {
if(best_shelf_idx != -1) {
Kit_SetItemAllocation(
item,
+ surface,
best_shelf_idx,
atlas->shelves[best_shelf_idx].count,
atlas->shelves[best_shelf_idx].width,
best_shelf_y);
- atlas->shelves[best_shelf_idx].width += item->surface->w;
+ atlas->shelves[best_shelf_idx].width += surface->w;
atlas->shelves[best_shelf_idx].count += 1;
return 0;
- } else if(total_remaining_h >= item->surface->h) {
- atlas->shelves[shelf_idx].width = item->surface->w;
- atlas->shelves[shelf_idx].height = item->surface->h;
+ } else if(total_remaining_h >= surface->h) {
+ atlas->shelves[shelf_idx].width = surface->w;
+ atlas->shelves[shelf_idx].height = surface->h;
atlas->shelves[shelf_idx].count = 1;
Kit_SetItemAllocation(
item,
+ surface,
shelf_idx,
0,
0,
@@ -147,105 +129,59 @@ int Kit_FindFreeAtlasSlot(Kit_TextureAtlas *atlas, Kit_TextureAtlasItem *item) {
return 1; // Can't fit!
}
-int Kit_AllocateAtlasSurfaces(Kit_TextureAtlas *atlas) {
- assert(atlas != NULL);
-
- 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
- goto exit_0;
- }
- }
- return 0;
-
-exit_0:
- return 1;
-}
-
-void Kit_BlitAtlasSurfaces(Kit_TextureAtlas *atlas, SDL_Texture *texture) {
+void Kit_CheckAtlasTextureSize(Kit_TextureAtlas *atlas, SDL_Texture *texture) {
assert(atlas != NULL);
assert(texture != NULL);
- Kit_TextureAtlasItem *item;
- for(int i = 0; i < atlas->cur_items; i++) {
- item = &atlas->items[i];
- if(item->cur_shelf == -1) // Skip if not allocated
- continue;
- if(!item->is_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);
- int ret = 0;
-
- // Check if texture size has changed
+ // Check if texture size has changed, and clear content if it has.
int texture_w;
int 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;
}
-
- // Allocate spots for all surfaces in the result texture. If there is not enough room for
- // everything, we need to show it to the caller.
- if(Kit_AllocateAtlasSurfaces(atlas) != 0) {
- ret = 1;
- }
-
- // Blit unblitted surfaces to texture
- Kit_BlitAtlasSurfaces(atlas, texture);
- return ret;
}
int Kit_GetAtlasItems(const Kit_TextureAtlas *atlas, SDL_Rect *sources, SDL_Rect *targets, int limit) {
assert(atlas != NULL);
assert(limit >= 0);
- int count = 0;
- for(int i = 0; i < min(atlas->cur_items, limit); i++) {
+ int max_count = min(atlas->cur_items, limit);
+ for(int i = 0; i < max_count; i++) {
Kit_TextureAtlasItem *item = &atlas->items[i];
- if(!item->is_copied)
- continue;
-
if(sources != NULL)
- memcpy(&sources[count], &item->source, sizeof(SDL_Rect));
+ memcpy(&sources[i], &item->source, sizeof(SDL_Rect));
if(targets != NULL)
- memcpy(&targets[count], &item->target, sizeof(SDL_Rect));
-
- count++;
+ memcpy(&targets[i], &item->target, sizeof(SDL_Rect));
}
- return count;
+ return max_count;
}
-int Kit_AddAtlasItem(Kit_TextureAtlas *atlas, SDL_Surface *surface, const SDL_Rect *target) {
+int Kit_AddAtlasItem(Kit_TextureAtlas *atlas, SDL_Texture *texture, SDL_Surface *surface, const SDL_Rect *target) {
assert(atlas != NULL);
assert(surface != NULL);
assert(target != NULL);
- Kit_TextureAtlasItem *item;
+ // Make sure there is still room
+ if(atlas->cur_items >= atlas->max_items)
+ return -1;
+
+ // Create a new item
+ Kit_TextureAtlasItem item;
+ memset(&item, 0, sizeof(Kit_TextureAtlasItem));
+ memcpy(&item.target, target, sizeof(SDL_Rect));
+ item.cur_shelf = -1;
+ item.cur_slot = -1;
- if(atlas->cur_items >= atlas->max_items) {
- return -1; // Cannot fit, buffer full!
+ // Allocate space for the new item
+ if(Kit_FindFreeAtlasSlot(atlas, surface, &item) != 0) {
+ return -1;
}
- item = &atlas->items[atlas->cur_items++];
- item->cur_shelf = -1;
- item->cur_slot = -1;
- item->is_copied = false;
- item->surface = surface;
- item->surface->refcount++; // We dont want to needlessly copy; instead increase refcount.
- memset(&item->source, 0, sizeof(SDL_Rect));
- memcpy(&item->target, target, sizeof(SDL_Rect));
+ // And update texture with the surface
+ SDL_UpdateTexture(texture, &item.source, surface->pixels, surface->pitch);
+
+ // Room found, add item to the atlas
+ memcpy(&atlas->items[atlas->cur_items++], &item, sizeof(Kit_TextureAtlasItem));
return 0;
}
diff --git a/src/internal/subtitle/kitsubtitle.c b/src/internal/subtitle/kitsubtitle.c
index b868615..d6bed5d 100644
--- a/src/internal/subtitle/kitsubtitle.c
+++ b/src/internal/subtitle/kitsubtitle.c
@@ -173,7 +173,7 @@ void Kit_SetSubtitleDecoderSize(Kit_Decoder *dec, int screen_w, int screen_h) {
Kit_SetSubtitleRendererSize(subtitle_dec->renderer, screen_w, screen_h);
}
-int Kit_GetSubtitleDecoderData(Kit_Decoder *dec, SDL_Texture *texture, SDL_Rect *sources, SDL_Rect *targets, int limit) {
+void Kit_GetSubtitleDecoderTexture(Kit_Decoder *dec, SDL_Texture *texture) {
assert(dec != NULL);
assert(texture != NULL);
@@ -181,17 +181,10 @@ int Kit_GetSubtitleDecoderData(Kit_Decoder *dec, SDL_Texture *texture, SDL_Rect
double sync_ts = _GetSystemTime() - dec->clock_sync;
// Tell the renderer to render content to atlas
- Kit_GetSubtitleRendererData(subtitle_dec->renderer, subtitle_dec->atlas, sync_ts);
-
- // 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;
- }
-
- // Next, get targets and sources of visible atlas items
- int item_count = Kit_GetAtlasItems(subtitle_dec->atlas, sources, targets, limit);
+ Kit_GetSubtitleRendererData(subtitle_dec->renderer, subtitle_dec->atlas, texture, sync_ts);
+}
- // All done
- return item_count;
+int Kit_GetSubtitleDecoderInfo(Kit_Decoder *dec, SDL_Texture *texture, SDL_Rect *sources, SDL_Rect *targets, int limit) {
+ Kit_SubtitleDecoder *subtitle_dec = dec->userdata;
+ return Kit_GetAtlasItems(subtitle_dec->atlas, sources, targets, limit);
}
diff --git a/src/internal/subtitle/renderers/kitsubass.c b/src/internal/subtitle/renderers/kitsubass.c
index afac077..7db72dd 100644
--- a/src/internal/subtitle/renderers/kitsubass.c
+++ b/src/internal/subtitle/renderers/kitsubass.c
@@ -67,7 +67,7 @@ static void ren_close_ass_cb(Kit_SubtitleRenderer *ren) {
free(ass_ren);
}
-static int ren_get_ass_data_cb(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, double current_pts) {
+static int ren_get_ass_data_cb(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, SDL_Texture *texture, double current_pts) {
Kit_ASSSubtitleRenderer *ass_ren = ren->userdata;
SDL_Surface *dst = NULL;
ASS_Image *src = NULL;
@@ -86,6 +86,7 @@ static int ren_get_ass_data_cb(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atla
// There was some change, process images and add them to atlas
Kit_ClearAtlasContent(atlas);
+ Kit_CheckAtlasTextureSize(atlas, texture);
for(; src; src = src->next) {
if(src->w == 0 || src->h == 0)
continue;
@@ -96,7 +97,7 @@ static int ren_get_ass_data_cb(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atla
target.y = src->dst_y;
target.w = dst->w;
target.h = dst->h;
- Kit_AddAtlasItem(atlas, dst, &target);
+ Kit_AddAtlasItem(atlas, texture, dst, &target);
SDL_FreeSurface(dst);
}
diff --git a/src/internal/subtitle/renderers/kitsubimage.c b/src/internal/subtitle/renderers/kitsubimage.c
index 890a829..0cd0cce 100644
--- a/src/internal/subtitle/renderers/kitsubimage.c
+++ b/src/internal/subtitle/renderers/kitsubimage.c
@@ -57,17 +57,20 @@ static void ren_render_image_cb(Kit_SubtitleRenderer *ren, void *sub_src, double
}
}
-static int ren_get_img_data_cb(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, double current_pts) {
+static int ren_get_img_data_cb(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, SDL_Texture *texture, double current_pts) {
Kit_ImageSubtitleRenderer *img_ren = ren->userdata;
Kit_SubtitlePacket *packet = NULL;
- // Clean dead packets
+ Kit_CheckAtlasTextureSize(atlas, texture);
while((packet = Kit_PeekDecoderOutput(ren->dec)) != NULL) {
+ // Clear dead packets
if(packet->pts_end < current_pts) {
Kit_AdvanceDecoderOutput(ren->dec);
Kit_FreeSubtitlePacket(packet);
continue;
}
+
+ // Show visible ones
if(packet->pts_start < current_pts) {
if(packet->clear) {
Kit_ClearAtlasContent(atlas);
@@ -78,7 +81,7 @@ static int ren_get_img_data_cb(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atla
target.y = packet->y * img_ren->scale_y;
target.w = packet->surface->w * img_ren->scale_x;
target.h = packet->surface->h * img_ren->scale_y;
- Kit_AddAtlasItem(atlas, packet->surface, &target);
+ Kit_AddAtlasItem(atlas, texture, packet->surface, &target);
}
Kit_AdvanceDecoderOutput(ren->dec);
Kit_FreeSubtitlePacket(packet);
diff --git a/src/internal/subtitle/renderers/kitsubrenderer.c b/src/internal/subtitle/renderers/kitsubrenderer.c
index f998899..77ad201 100644
--- a/src/internal/subtitle/renderers/kitsubrenderer.c
+++ b/src/internal/subtitle/renderers/kitsubrenderer.c
@@ -24,11 +24,11 @@ void Kit_RunSubtitleRenderer(Kit_SubtitleRenderer *ren, void *src, double start_
ren->ren_render(ren, src, start_pts, end_pts);
}
-int Kit_GetSubtitleRendererData(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, double current_pts) {
+int Kit_GetSubtitleRendererData(Kit_SubtitleRenderer *ren, Kit_TextureAtlas *atlas, SDL_Texture *texture, double current_pts) {
if(ren == NULL)
return 0;
if(ren->ren_get_data != NULL)
- return ren->ren_get_data(ren, atlas, current_pts);
+ return ren->ren_get_data(ren, atlas, texture, current_pts);
return 0;
}
diff --git a/src/kitplayer.c b/src/kitplayer.c
index 188f6ce..5220945 100644
--- a/src/kitplayer.c
+++ b/src/kitplayer.c
@@ -301,15 +301,19 @@ int Kit_GetPlayerSubtitleData(Kit_Player *player, SDL_Texture *texture, SDL_Rect
return 0;
}
- // If paused or stopped, do nothing
+ // If paused, just return the current items
if(player->state == KIT_PAUSED) {
- return 0;
+ return Kit_GetSubtitleDecoderInfo(dec, texture, sources, targets, limit);
}
+
+ // If stopped, do nothing.
if(player->state == KIT_STOPPED) {
return 0;
}
- return Kit_GetSubtitleDecoderData(dec, texture, sources, targets, limit);
+ // Refresh texture, then refresh rects and return number of items in the texture.
+ Kit_GetSubtitleDecoderTexture(dec, texture);
+ return Kit_GetSubtitleDecoderInfo(dec, texture, sources, targets, limit);
}
void Kit_GetPlayerInfo(const Kit_Player *player, Kit_PlayerInfo *info) {