From 5dc94e8cb8eefa6f708de5054e44477cd4598d78 Mon Sep 17 00:00:00 2001 From: Tuomas Virtanen Date: Sun, 17 Jan 2016 03:17:22 +0200 Subject: This and that --- examples/example_video.c | 29 ++--- include/kitchensink/internal/kitlist.h | 26 ++++ include/kitchensink/kitplayer.h | 4 +- src/kitlist.c | 78 ++++++++++++ src/kitplayer.c | 212 ++++++++++++++++----------------- 5 files changed, 222 insertions(+), 127 deletions(-) create mode 100644 include/kitchensink/internal/kitlist.h create mode 100644 src/kitlist.c 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 #include +#include #include @@ -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 +#include +#include + +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) { -- cgit v1.2.3