summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/example_video.c29
-rw-r--r--include/kitchensink/internal/kitlist.h26
-rw-r--r--include/kitchensink/kitplayer.h4
-rw-r--r--src/kitlist.c78
-rw-r--r--src/kitplayer.c212
5 files changed, 222 insertions, 127 deletions
diff --git a/examples/example_video.c b/examples/example_video.c
index 61659e9..b36bbeb 100644
--- a/examples/example_video.c
+++ b/examples/example_video.c
@@ -19,7 +19,7 @@ void render_gui(SDL_Renderer *renderer, double percent) {
SDL_RenderGetLogicalSize(renderer, &size_w, &size_h);
// Render progress bar
- SDL_SetRenderDrawColor(renderer, 50, 50, 50, 192);
+ SDL_SetRenderDrawColor(renderer, 50, 50, 50, 255);
SDL_Rect progress_border;
progress_border.x = 28;
progress_border.y = size_h - 61;
@@ -27,7 +27,7 @@ void render_gui(SDL_Renderer *renderer, double percent) {
progress_border.h = 22;
SDL_RenderFillRect(renderer, &progress_border);
- SDL_SetRenderDrawColor(renderer, 155, 155, 155, 128);
+ SDL_SetRenderDrawColor(renderer, 155, 155, 155, 255);
SDL_Rect progress_bottom;
progress_bottom.x = 30;
progress_bottom.y = size_h - 60;
@@ -35,7 +35,7 @@ void render_gui(SDL_Renderer *renderer, double percent) {
progress_bottom.h = 20;
SDL_RenderFillRect(renderer, &progress_bottom);
- SDL_SetRenderDrawColor(renderer, 100, 100, 100, 128);
+ SDL_SetRenderDrawColor(renderer, 100, 100, 100, 255);
SDL_Rect progress_top;
progress_top.x = 30;
progress_top.y = size_h - 60;
@@ -79,6 +79,9 @@ int main(int argc, char *argv[]) {
return 1;
}
+ // Attempt to acquire opengl driver context
+ SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
+
// Create a resizable window.
window = SDL_CreateWindow("Example Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 720, SDL_WINDOW_RESIZABLE);
if(window == NULL) {
@@ -93,6 +96,12 @@ int main(int argc, char *argv[]) {
return 1;
}
+ // We want to alphablend textures, so switch that on
+ if(SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND) < 0) {
+ fprintf(stderr, "Unable to set blendmode!\n");
+ return 1;
+ }
+
// Ask for linear texture scaling (better quality)
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
@@ -184,16 +193,6 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "Error while attempting to create a video texture\n");
return 1;
}
- SDL_Texture *subtitle_tex = SDL_CreateTexture(
- renderer,
- SDL_PIXELFORMAT_ABGR8888,
- SDL_TEXTUREACCESS_STATIC,
- pinfo.video.width,
- pinfo.video.height);
- if(subtitle_tex == NULL) {
- fprintf(stderr, "Error while attempting to create a subtitle texture\n");
- return 1;
- }
fflush(stderr);
@@ -298,9 +297,8 @@ int main(int argc, char *argv[]) {
// Refresh videotexture and render it
Kit_GetVideoData(player, video_tex);
- Kit_GetSubtitleData(player, subtitle_tex);
SDL_RenderCopy(renderer, video_tex, NULL, NULL);
- //SDL_RenderCopy(renderer, subtitle_tex, NULL, NULL);
+ Kit_GetSubtitleData(player, renderer);
// Render GUI
if(gui_enabled) {
@@ -313,7 +311,6 @@ int main(int argc, char *argv[]) {
}
SDL_DestroyTexture(video_tex);
- SDL_DestroyTexture(subtitle_tex);
SDL_CloseAudioDevice(audio_dev);
Kit_ClosePlayer(player);
diff --git a/include/kitchensink/internal/kitlist.h b/include/kitchensink/internal/kitlist.h
new file mode 100644
index 0000000..85e3e3f
--- /dev/null
+++ b/include/kitchensink/internal/kitlist.h
@@ -0,0 +1,26 @@
+#ifndef KITLIST_H
+#define KITLIST_H
+
+#include "kitchensink/kitconfig.h"
+
+typedef struct Kit_List Kit_List;
+
+typedef void (*Kit_ListFreeCallback)(void*);
+
+struct Kit_List {
+ unsigned int size;
+ unsigned int length;
+ Kit_ListFreeCallback free_cb;
+ void **data;
+};
+
+KIT_LOCAL Kit_List* Kit_CreateList(unsigned int size, Kit_ListFreeCallback free_cb);
+KIT_LOCAL void Kit_DestroyList(Kit_List *list);
+
+KIT_LOCAL void Kit_ClearList(Kit_List *list);
+KIT_LOCAL void Kit_RemoveFromList(Kit_List *list, unsigned int iterator);
+KIT_LOCAL void* Kit_IterateList(const Kit_List *list, unsigned int *iterator);
+KIT_LOCAL int Kit_WriteList(Kit_List *list, void *ptr);
+KIT_LOCAL int Kit_GetListLength(const Kit_List *list);
+
+#endif // KITLIST_H
diff --git a/include/kitchensink/kitplayer.h b/include/kitchensink/kitplayer.h
index 7e3ba53..1d1f5f9 100644
--- a/include/kitchensink/kitplayer.h
+++ b/include/kitchensink/kitplayer.h
@@ -6,6 +6,7 @@
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_thread.h>
+#include <SDL2/SDL_surface.h>
#include <stdbool.h>
@@ -70,7 +71,6 @@ typedef struct Kit_Player {
void *vbuffer; ///< Video stream buffer
void *sbuffer; ///< Subtitle stream buffer
void *cbuffer; ///< Control stream buffer
- void *active_subs; ///< Active subtitles buffer
// FFmpeg internal state
void *vcodec_ctx; ///< FFmpeg: Video codec context
@@ -104,7 +104,7 @@ KIT_API void Kit_ClosePlayer(Kit_Player *player);
KIT_API int Kit_UpdatePlayer(Kit_Player *player);
KIT_API int Kit_GetVideoData(Kit_Player *player, SDL_Texture *texture);
-KIT_API int Kit_GetSubtitleData(Kit_Player *player, SDL_Texture *texture);
+KIT_API int Kit_GetSubtitleData(Kit_Player *player, SDL_Renderer *renderer);
KIT_API int Kit_GetAudioData(Kit_Player *player, unsigned char *buffer, int length, int cur_buf_len);
KIT_API void Kit_GetPlayerInfo(const Kit_Player *player, Kit_PlayerInfo *info);
diff --git a/src/kitlist.c b/src/kitlist.c
new file mode 100644
index 0000000..9926aa1
--- /dev/null
+++ b/src/kitlist.c
@@ -0,0 +1,78 @@
+#include "kitchensink/internal/kitlist.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+Kit_List* Kit_CreateList(unsigned int size, Kit_ListFreeCallback free_cb) {
+ Kit_List *m = calloc(1, sizeof(Kit_List));
+ if(m == NULL) {
+ return NULL;
+ }
+ m->size = size;
+ m->free_cb = free_cb;
+ m->data = calloc(size, sizeof(void*));
+ if(m->data == NULL) {
+ free(m);
+ return NULL;
+ }
+ return m;
+}
+
+void Kit_DestroyList(Kit_List *list) {
+ if(list == NULL) return;
+ Kit_ClearList(list);
+ free(list->data);
+ free(list);
+}
+
+void Kit_ClearList(Kit_List *list) {
+ assert(list != NULL);
+ for(unsigned int i = 0; i < list->size; i++) {
+ if(list->data[i] != NULL) {
+ list->free_cb(list->data[i]);
+ list->data[i] = NULL;
+ }
+ }
+}
+
+void Kit_RemoveFromList(Kit_List *list, unsigned int iterator) {
+ assert(list != NULL);
+ list->free_cb(list->data[iterator-1]);
+ list->data[iterator-1] = NULL;
+ list->length--;
+}
+
+void* Kit_IterateList(const Kit_List *list, unsigned int *iterator) {
+ assert(list != NULL);
+ assert(iterator != NULL);
+ while((*iterator) < list->size) {
+ void *ptr = list->data[(*iterator)];
+ *iterator += 1;
+ if(ptr != NULL) {
+ return ptr;
+ }
+ }
+ return NULL;
+}
+
+int Kit_WriteList(Kit_List *list, void *ptr) {
+ assert(list != NULL);
+ assert(ptr != NULL);
+ if(list->length >= list->size) {
+ return 1;
+ }
+ for(unsigned int i = 0; i < list->size; i++) {
+ if(list->data[i] == NULL) {
+ list->data[i] = ptr;
+ list->length++;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int Kit_GetListLength(const Kit_List *list) {
+ assert(list != NULL);
+ return list->length;
+}
diff --git a/src/kitplayer.c b/src/kitplayer.c
index 1b55ca4..23aa56a 100644
--- a/src/kitplayer.c
+++ b/src/kitplayer.c
@@ -30,8 +30,7 @@
#define KIT_VBUFFERSIZE 3
#define KIT_ABUFFERSIZE 64
#define KIT_CBUFFERSIZE 8
-#define KIT_SBUFFERSIZE 32
-#define KIT_SLISTSIZE 16
+#define KIT_SBUFFERSIZE 16
typedef enum Kit_ControlPacketType {
KIT_CONTROL_SEEK,
@@ -55,8 +54,11 @@ typedef struct Kit_ControlPacket {
} Kit_ControlPacket;
typedef struct Kit_SubtitlePacket {
- double pts;
- AVSubtitle *subtitle;
+ double pts_start;
+ double pts_end;
+ SDL_Rect *rect;
+ SDL_Surface *surface;
+ SDL_Texture *texture;
} Kit_SubtitlePacket;
static int _InitCodecs(Kit_Player *player, const Kit_Source *src) {
@@ -281,16 +283,23 @@ static void _FreeControlPacket(void *ptr) {
}
-static Kit_SubtitlePacket* _CreateSubtitlePacket(AVSubtitle *sub, double pts) {
+static Kit_SubtitlePacket* _CreateSubtitlePacket(SDL_Rect *rect, SDL_Surface *surface, double pts_start, double pts_end) {
Kit_SubtitlePacket *p = calloc(1, sizeof(Kit_SubtitlePacket));
- p->pts = pts;
- p->subtitle = sub;
+ p->pts_start = pts_start;
+ p->pts_end = pts_end;
+ p->surface = surface;
+ p->rect = rect;
+ p->texture = NULL; // Cached texture
return p;
}
static void _FreeSubtitlePacket(void *ptr) {
Kit_SubtitlePacket *packet = ptr;
- avsubtitle_free(packet->subtitle);
+ SDL_FreeSurface(packet->surface);
+ if(packet->texture) {
+ SDL_DestroyTexture(packet->texture);
+ }
+ free(packet->rect);
free(packet);
}
@@ -442,6 +451,42 @@ static void _HandleAudioPacket(Kit_Player *player, AVPacket *packet) {
}
}
+static Kit_SubtitlePacket* _HandleBitmapSubtitle(Kit_Player *player, double pts, AVSubtitle *sub, AVSubtitleRect *rect) {
+ if(rect->nb_colors == 256) {
+ // Paletted image based subtitles. Convert and set palette.
+ SDL_Surface *s = SDL_CreateRGBSurfaceFrom(
+ rect->pict.data[0],
+ rect->w, rect->h, 8,
+ rect->pict.linesize[0],
+ 0, 0, 0, 0);
+
+ SDL_Color a = {255,255,255,255};
+ SDL_Color b = {0,0,0,255};
+ SDL_Color c = {0,0,0,0};
+ SDL_SetPaletteColors(s->format->palette, &a, 0, 1);
+ SDL_SetPaletteColors(s->format->palette, &b, 1, 1);
+ SDL_SetPaletteColors(s->format->palette, &c, 0xff, 1);
+
+ SDL_Surface *tmp = SDL_CreateRGBSurface(
+ 0, rect->w, rect->h, 8,
+ 0, 0, 0, 0);
+ SDL_BlitSurface(s, NULL, tmp, NULL);
+ SDL_FreeSurface(s);
+
+ SDL_Rect *dst_rect = malloc(sizeof(SDL_Rect));
+ dst_rect->x = rect->x;
+ dst_rect->y = rect->y;
+ dst_rect->w = rect->w;
+ dst_rect->h = rect->h;
+
+ double start = pts + sub->start_display_time / 1000.0;
+ double end = pts + sub->end_display_time / 1000.0;
+
+ return _CreateSubtitlePacket(dst_rect, tmp, start, end);
+ }
+ return NULL;
+}
+
static void _HandleSubtitlePacket(Kit_Player *player, AVPacket *packet) {
assert(player != NULL);
assert(packet != NULL);
@@ -467,21 +512,33 @@ static void _HandleSubtitlePacket(Kit_Player *player, AVPacket *packet) {
pts *= av_q2d(fmt_ctx->streams[player->src->sstream_idx]->time_base);
}
- // Lock, write to subtitle buffer, unlock
- Kit_SubtitlePacket *spacket = _CreateSubtitlePacket(sub, pts);
- bool done = false;
- if(SDL_LockMutex(player->smutex) == 0) {
- if(Kit_WriteBuffer((Kit_Buffer*)player->sbuffer, spacket) == 0) {
- done = true;
- }
- SDL_UnlockMutex(player->smutex);
+ // Convert subtitles to SDL_Surface and create a packet
+ Kit_SubtitlePacket *spacket;
+ for(int r = 0; r < sub->num_rects; r++) {
+ AVSubtitleRect *rect = sub->rects[r];
+ switch(rect->type) {
+ case SUBTITLE_BITMAP: spacket = _HandleBitmapSubtitle(player, pts, sub, rect); break;
+ case SUBTITLE_TEXT: break;
+ case SUBTITLE_ASS: break;
+ default: break;
+ }
}
- // Couldn't write packet, free memory
- if(!done) {
- _FreeSubtitlePacket(spacket);
+ // Lock, write to subtitle buffer, unlock
+ if(spacket != NULL) {
+ bool done = false;
+ if(SDL_LockMutex(player->smutex) == 0) {
+ if(Kit_WriteList((Kit_List*)player->sbuffer, spacket) == 0) {
+ done = true;
+ }
+ SDL_UnlockMutex(player->smutex);
+ }
+
+ // Couldn't write packet, free memory
+ if(!done) {
+ _FreeSubtitlePacket(spacket);
+ }
}
- return;
}
}
@@ -511,7 +568,7 @@ static void _HandleFlushCommand(Kit_Player *player, Kit_ControlPacket *packet) {
SDL_UnlockMutex(player->vmutex);
}
if(SDL_LockMutex(player->smutex) == 0) {
- Kit_ClearBuffer((Kit_Buffer*)player->sbuffer);
+ Kit_ClearList((Kit_List*)player->sbuffer);
SDL_UnlockMutex(player->smutex);
}
}
@@ -739,15 +796,9 @@ Kit_Player* Kit_CreatePlayer(const Kit_Source *src) {
player->sformat.is_enabled = true;
player->sformat.stream_idx = src->sstream_idx;
- player->sbuffer = Kit_CreateBuffer(KIT_SBUFFERSIZE, _FreeSubtitlePacket);
+ player->sbuffer = Kit_CreateList(KIT_SBUFFERSIZE, _FreeSubtitlePacket);
if(player->sbuffer == NULL) {
- Kit_SetError("Unable to initialize subtitle ringbuffer");
- goto error;
- }
-
- player->active_subs = Kit_CreateList(KIT_SLISTSIZE, _FreeSubtitlePacket);
- if(player->active_subs == NULL) {
- Kit_SetError("Unable to initialize subtitle active buffer");
+ Kit_SetError("Unable to initialize active subtitle list");
goto error;
}
}
@@ -812,9 +863,8 @@ error:
Kit_DestroyBuffer((Kit_Buffer*)player->vbuffer);
Kit_DestroyBuffer((Kit_Buffer*)player->abuffer);
- Kit_DestroyBuffer((Kit_Buffer*)player->sbuffer);
Kit_DestroyBuffer((Kit_Buffer*)player->cbuffer);
- Kit_DestroyList((Kit_List*)player->active_subs);
+ Kit_DestroyList((Kit_List*)player->sbuffer);
if(player->sws != NULL) {
sws_freeContext((struct SwsContext *)player->sws);
@@ -866,9 +916,8 @@ void Kit_ClosePlayer(Kit_Player *player) {
// Free local audio buffers
Kit_DestroyBuffer((Kit_Buffer*)player->cbuffer);
Kit_DestroyBuffer((Kit_Buffer*)player->abuffer);
- Kit_DestroyBuffer((Kit_Buffer*)player->sbuffer);
Kit_DestroyBuffer((Kit_Buffer*)player->vbuffer);
- Kit_DestroyList((Kit_List*)player->active_subs);
+ Kit_DestroyList((Kit_List*)player->sbuffer);
// Free the player structure itself
free(player);
@@ -956,7 +1005,7 @@ int Kit_GetVideoData(Kit_Player *player, SDL_Texture *texture) {
return 0;
}
-int Kit_GetSubtitleData(Kit_Player *player, SDL_Texture *texture) {
+int Kit_GetSubtitleData(Kit_Player *player, SDL_Renderer *renderer) {
assert(player != NULL);
// If there is no audio stream, don't bother.
@@ -964,7 +1013,7 @@ int Kit_GetSubtitleData(Kit_Player *player, SDL_Texture *texture) {
return 0;
}
- assert(texture != NULL);
+ assert(renderer != NULL);
// If paused or stopped, do nothing
if(player->state == KIT_PAUSED) {
@@ -974,51 +1023,34 @@ int Kit_GetSubtitleData(Kit_Player *player, SDL_Texture *texture) {
return 0;
}
+ unsigned int it;
+ Kit_SubtitlePacket *packet = NULL;
+
// Current sync timestamp
double cur_subtitle_ts = _GetSystemTime() - player->clock_sync;
// Read a packet from buffer, if one exists. Stop here if not.
- Kit_SubtitlePacket *packet = NULL;
- Kit_SubtitlePacket *n_packet = NULL;
if(SDL_LockMutex(player->smutex) == 0) {
- packet = (Kit_SubtitlePacket*)Kit_PeekBuffer((Kit_Buffer*)player->sbuffer);
- if(packet == NULL) {
- SDL_UnlockMutex(player->smutex);
- return 0;
+ // Check if refresh is required and remove old subtitles
+ it = 0;
+ while((packet = Kit_IterateList((Kit_List*)player->sbuffer, &it)) != NULL) {
+ if(packet->pts_start - SUBTITLE_SYNC_THRESHOLD < cur_subtitle_ts) {
+ Kit_RemoveFromList((Kit_List*)player->sbuffer, it);
+ }
+ else if(packet->pts_end + SUBTITLE_SYNC_THRESHOLD < cur_subtitle_ts) {
+ Kit_RemoveFromList((Kit_List*)player->sbuffer, it);
+ }
}
- // Print some data
- double start_time = packet->pts;
- double end_time = packet->pts + (packet->subtitle->end_display_time / 1000.0);
-
- // Check if we want the packet
- if(start_time > cur_subtitle_ts + SUBTITLE_SYNC_THRESHOLD) {
- // Video is ahead, don't show yet.
- SDL_UnlockMutex(player->smutex);
- return 0;
- } else if(end_time < cur_subtitle_ts - SUBTITLE_SYNC_THRESHOLD) {
-
- // subtitles are lagging, skip until we find a good PTS to continue from.
- while(packet != NULL) {
- Kit_AdvanceBuffer((Kit_Buffer*)player->sbuffer);
- n_packet = (Kit_SubtitlePacket*)Kit_PeekBuffer((Kit_Buffer*)player->sbuffer);
- if(n_packet == NULL) {
- break;
- }
- _FreeSubtitlePacket(packet);
- packet = n_packet;
- if(packet->pts > cur_subtitle_ts - SUBTITLE_SYNC_THRESHOLD) {
- break;
- }
+ // Render subtitle bitmaps
+ it = 0;
+ while((packet = Kit_IterateList((Kit_List*)player->sbuffer, &it)) != NULL) {
+ if(packet->texture == NULL) {
+ packet->texture = SDL_CreateTextureFromSurface(renderer, packet->surface);
}
+ SDL_RenderCopy(renderer, packet->texture, NULL, packet->rect);
}
- // Advance buffer one subtitle forwards
- Kit_AdvanceBuffer((Kit_Buffer*)player->sbuffer);
-
- // Add subtitle to the list of currently active subtitles
- Kit_WriteList((Kit_List*)player->active_subs, packet);
-
// Unlock subtitle buffer mutex.
SDL_UnlockMutex(player->smutex);
} else {
@@ -1026,45 +1058,7 @@ int Kit_GetSubtitleData(Kit_Player *player, SDL_Texture *texture) {
return 1;
}
- // handle all active subtitles
- unsigned int it = 0;
- double start, end;
- Kit_SubtitlePacket *s_packet = NULL;
- while((s_packet = Kit_IterateList((Kit_List*)player->active_subs, &it)) != NULL) {
- start = s_packet->pts + s_packet->subtitle->start_display_time / 1000.0;
- end = s_packet->pts + s_packet->subtitle->end_display_time / 1000.0;
-
- // If subtitle is displayed and done, remove it from the list and free the packet.
- // Otherwise, if subtitle is started, render it.
- // If subtitle is still waiting to start, do nothing.
- if(end < cur_subtitle_ts || cur_subtitle_ts < s_packet->pts - SUBTITLE_SYNC_THRESHOLD) {
- fprintf(stderr, "Subtitle is done: %7.3f\n", end);
- Kit_RemoveFromList((Kit_List*)player->active_subs, it);
- _FreeSubtitlePacket(s_packet);
- }
- else if(start >= cur_subtitle_ts) {
- for(int r = 0; r < s_packet->subtitle->num_rects; r++) {
- AVSubtitleRect *rect = s_packet->subtitle->rects[r];
- switch(rect->type) {
- case SUBTITLE_BITMAP:
- fprintf(stderr, "x = %d, y = %d, w = %d, h = %d\n", rect->x, rect->y, rect->w, rect->h);
- break;
- case SUBTITLE_TEXT:
- fprintf(stderr, "text = %s\n", rect->text);
- break;
- case SUBTITLE_ASS:
- fprintf(stderr, "text = %s\n", rect->ass);
- break;
- default:
- break;
- }
- }
- fprintf(stderr, "Subtitle is started: %7.3f\n", s_packet->pts + start);
- }
- fflush(stderr);
- }
-
- return 1;
+ return 0;
}
int Kit_GetAudioData(Kit_Player *player, unsigned char *buffer, int length, int cur_buf_len) {