summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--include/kitchensink/internal/kitaudio.h28
-rw-r--r--include/kitchensink/internal/kitcontrol.h24
-rw-r--r--include/kitchensink/internal/kithelpers.h11
-rw-r--r--include/kitchensink/internal/kitsubtitle.h24
-rw-r--r--include/kitchensink/internal/kitvideo.h22
-rw-r--r--include/kitchensink/kitchensink.h1
-rw-r--r--include/kitchensink/kitformats.h29
-rw-r--r--include/kitchensink/kitplayer.h24
-rw-r--r--src/kitaudio.c155
-rw-r--r--src/kitcontrol.c82
-rw-r--r--src/kithelpers.c30
-rw-r--r--src/kitplayer.c632
-rw-r--r--src/kitsubtitle.c234
-rw-r--r--src/kitvideo.c128
15 files changed, 775 insertions, 651 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 14e4e3b..16ddf7c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,7 +13,7 @@ add_definitions(
)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -std=c99")
-set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -pedantic -Werror -fno-omit-frame-pointer")
+set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -pedantic -Werror -fno-omit-frame-pointer -Wno-deprecated-declarations")
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -ggdb -O2 -fno-omit-frame-pointer -DNDEBUG")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2 -DNDEBUG")
set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} -Os -DNDEBUG")
diff --git a/include/kitchensink/internal/kitaudio.h b/include/kitchensink/internal/kitaudio.h
new file mode 100644
index 0000000..e1c9ffb
--- /dev/null
+++ b/include/kitchensink/internal/kitaudio.h
@@ -0,0 +1,28 @@
+#ifndef KITAUDIO_H
+#define KITAUDIO_H
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+#include <libavformat/avformat.h>
+
+#include "kitchensink/kitconfig.h"
+#include "kitchensink/kitformats.h"
+#include "kitchensink/kitplayer.h"
+#include "kitchensink/internal/kitringbuffer.h"
+
+#define KIT_ABUFFERSIZE 64
+
+typedef struct Kit_AudioPacket {
+ double pts;
+ size_t original_size;
+ Kit_RingBuffer *rb;
+} Kit_AudioPacket;
+
+KIT_LOCAL Kit_AudioPacket* _CreateAudioPacket(const char* data, size_t len, double pts);
+KIT_LOCAL void _FreeAudioPacket(void *ptr);
+KIT_LOCAL enum AVSampleFormat _FindAVSampleFormat(int format);
+KIT_LOCAL unsigned int _FindAVChannelLayout(int channels);
+KIT_LOCAL void _FindAudioFormat(enum AVSampleFormat fmt, int *bytes, bool *is_signed, unsigned int *format);
+KIT_LOCAL void _HandleAudioPacket(Kit_Player *player, AVPacket *packet);
+
+#endif // KITAUDIO_H
diff --git a/include/kitchensink/internal/kitcontrol.h b/include/kitchensink/internal/kitcontrol.h
new file mode 100644
index 0000000..eaad484
--- /dev/null
+++ b/include/kitchensink/internal/kitcontrol.h
@@ -0,0 +1,24 @@
+#ifndef KITCONTROL_H
+#define KITCONTROL_H
+
+#include "kitchensink/kitconfig.h"
+#include "kitchensink/kitplayer.h"
+
+#define KIT_CBUFFERSIZE 8
+
+typedef enum Kit_ControlPacketType {
+ KIT_CONTROL_SEEK,
+ KIT_CONTROL_FLUSH
+} Kit_ControlPacketType;
+
+typedef struct Kit_ControlPacket {
+ Kit_ControlPacketType type;
+ double value1;
+} Kit_ControlPacket;
+
+KIT_LOCAL Kit_ControlPacket* _CreateControlPacket(Kit_ControlPacketType type, double value1);
+KIT_LOCAL void _FreeControlPacket(void *ptr);
+KIT_LOCAL void _HandleFlushCommand(Kit_Player *player, Kit_ControlPacket *packet);
+KIT_LOCAL void _HandleSeekCommand(Kit_Player *player, Kit_ControlPacket *packet);
+
+#endif // KITCONTROL_H
diff --git a/include/kitchensink/internal/kithelpers.h b/include/kitchensink/internal/kithelpers.h
new file mode 100644
index 0000000..5b94a7a
--- /dev/null
+++ b/include/kitchensink/internal/kithelpers.h
@@ -0,0 +1,11 @@
+#ifndef KITHELPERS_H
+#define KITHELPERS_H
+
+#include <stdbool.h>
+#include <libavformat/avformat.h>
+#include "kitchensink/kitconfig.h"
+
+KIT_LOCAL double _GetSystemTime();
+KIT_LOCAL bool attachment_is_font(AVStream *stream);
+
+#endif // KITHELPERS_H
diff --git a/include/kitchensink/internal/kitsubtitle.h b/include/kitchensink/internal/kitsubtitle.h
new file mode 100644
index 0000000..4582744
--- /dev/null
+++ b/include/kitchensink/internal/kitsubtitle.h
@@ -0,0 +1,24 @@
+#ifndef KITSUBTITLE_H
+#define KITSUBTITLE_H
+
+#include <libavformat/avformat.h>
+
+#include "kitchensink/kitconfig.h"
+#include "kitchensink/kitplayer.h"
+
+#define KIT_SBUFFERSIZE 512
+
+typedef struct Kit_SubtitlePacket {
+ double pts_start;
+ double pts_end;
+ SDL_Rect *rect;
+ SDL_Surface *surface;
+ SDL_Texture *texture;
+} Kit_SubtitlePacket;
+
+KIT_LOCAL Kit_SubtitlePacket* _CreateSubtitlePacket(double pts_start, double pts_end, SDL_Rect *rect, SDL_Surface *surface);
+KIT_LOCAL void _FreeSubtitlePacket(void *ptr);
+KIT_LOCAL void _HandleBitmapSubtitle(Kit_SubtitlePacket** spackets, int *n, Kit_Player *player, double pts, AVSubtitle *sub, AVSubtitleRect *rect);
+KIT_LOCAL void _HandleSubtitlePacket(Kit_Player *player, AVPacket *packet);
+
+#endif // KITSUBTITLE_H
diff --git a/include/kitchensink/internal/kitvideo.h b/include/kitchensink/internal/kitvideo.h
new file mode 100644
index 0000000..f1cb4ff
--- /dev/null
+++ b/include/kitchensink/internal/kitvideo.h
@@ -0,0 +1,22 @@
+#ifndef KITVIDEO_H
+#define KITVIDEO_H
+
+#include <libavformat/avformat.h>
+
+#include "kitchensink/kitconfig.h"
+#include "kitchensink/kitplayer.h"
+
+#define KIT_VBUFFERSIZE 3
+
+typedef struct Kit_VideoPacket {
+ double pts;
+ AVFrame *frame;
+} Kit_VideoPacket;
+
+KIT_LOCAL Kit_VideoPacket* _CreateVideoPacket(AVFrame *frame, double pts);
+KIT_LOCAL void _FreeVideoPacket(void *ptr);
+KIT_LOCAL void _FindPixelFormat(enum AVPixelFormat fmt, unsigned int *out_fmt);
+KIT_LOCAL enum AVPixelFormat _FindAVPixelFormat(unsigned int fmt);
+KIT_LOCAL void _HandleVideoPacket(Kit_Player *player, AVPacket *packet);
+
+#endif // KITVIDEO_H
diff --git a/include/kitchensink/kitchensink.h b/include/kitchensink/kitchensink.h
index be318a5..6c5da35 100644
--- a/include/kitchensink/kitchensink.h
+++ b/include/kitchensink/kitchensink.h
@@ -3,6 +3,7 @@
#include "kitchensink/kitlib.h"
#include "kitchensink/kiterror.h"
+#include "kitchensink/kitformats.h"
#include "kitchensink/kitsource.h"
#include "kitchensink/kitplayer.h"
#include "kitchensink/kitutils.h"
diff --git a/include/kitchensink/kitformats.h b/include/kitchensink/kitformats.h
new file mode 100644
index 0000000..ef86826
--- /dev/null
+++ b/include/kitchensink/kitformats.h
@@ -0,0 +1,29 @@
+#ifndef KITFORMATS_H
+#define KITFORMATS_H
+
+#include <stdbool.h>
+
+typedef struct Kit_AudioFormat {
+ int stream_idx; ///< Stream index
+ bool is_enabled; ///< Is stream enabled
+ unsigned int format; ///< SDL Audio Format
+ bool is_signed; ///< Signedness
+ int bytes; ///< Bytes per sample per channel
+ int samplerate; ///< Sampling rate
+ int channels; ///< Channels
+} Kit_AudioFormat;
+
+typedef struct Kit_VideoFormat {
+ int stream_idx; ///< Stream index
+ bool is_enabled; ///< Is stream enabled
+ unsigned int format; ///< SDL Pixel Format
+ int width; ///< Width in pixels
+ int height; ///< Height in pixels
+} Kit_VideoFormat;
+
+typedef struct Kit_SubtitleFormat {
+ int stream_idx; ///< Stream index
+ bool is_enabled; ///< Is stream enabled
+} Kit_SubtitleFormat;
+
+#endif // KITFORMATS_H
diff --git a/include/kitchensink/kitplayer.h b/include/kitchensink/kitplayer.h
index b9ea1c7..9505208 100644
--- a/include/kitchensink/kitplayer.h
+++ b/include/kitchensink/kitplayer.h
@@ -3,6 +3,7 @@
#include "kitchensink/kitsource.h"
#include "kitchensink/kitconfig.h"
+#include "kitchensink/kitformats.h"
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_thread.h>
@@ -24,29 +25,6 @@ typedef enum Kit_PlayerState {
KIT_CLOSED ///< Playback is stopped and player is closing.
} Kit_PlayerState;
-typedef struct Kit_AudioFormat {
- int stream_idx; ///< Stream index
- bool is_enabled; ///< Is stream enabled
- unsigned int format; ///< SDL Audio Format
- bool is_signed; ///< Signedness
- int bytes; ///< Bytes per sample per channel
- int samplerate; ///< Sampling rate
- int channels; ///< Channels
-} Kit_AudioFormat;
-
-typedef struct Kit_VideoFormat {
- int stream_idx; ///< Stream index
- bool is_enabled; ///< Is stream enabled
- unsigned int format; ///< SDL Pixel Format
- int width; ///< Width in pixels
- int height; ///< Height in pixels
-} Kit_VideoFormat;
-
-typedef struct Kit_SubtitleFormat {
- int stream_idx; ///< Stream index
- bool is_enabled; ///< Is stream enabled
-} Kit_SubtitleFormat;
-
typedef struct Kit_Player {
// Local state
Kit_PlayerState state; ///< Playback state
diff --git a/src/kitaudio.c b/src/kitaudio.c
new file mode 100644
index 0000000..83385c8
--- /dev/null
+++ b/src/kitaudio.c
@@ -0,0 +1,155 @@
+#include <assert.h>
+
+#include <libavutil/samplefmt.h>
+#include <libswresample/swresample.h>
+#include <SDL2/SDL.h>
+
+#include "kitchensink/internal/kithelpers.h"
+#include "kitchensink/internal/kitbuffer.h"
+#include "kitchensink/internal/kitaudio.h"
+
+
+Kit_AudioPacket* _CreateAudioPacket(const char* data, size_t len, double pts) {
+ Kit_AudioPacket *p = calloc(1, sizeof(Kit_AudioPacket));
+ p->rb = Kit_CreateRingBuffer(len);
+ Kit_WriteRingBuffer(p->rb, data, len);
+ p->pts = pts;
+ return p;
+}
+
+void _FreeAudioPacket(void *ptr) {
+ Kit_AudioPacket *packet = ptr;
+ Kit_DestroyRingBuffer(packet->rb);
+ free(packet);
+}
+
+enum AVSampleFormat _FindAVSampleFormat(int format) {
+ switch(format) {
+ case AUDIO_U8: return AV_SAMPLE_FMT_U8;
+ case AUDIO_S16SYS: return AV_SAMPLE_FMT_S16;
+ case AUDIO_S32SYS: return AV_SAMPLE_FMT_S32;
+ default:
+ return AV_SAMPLE_FMT_NONE;
+ }
+}
+
+unsigned int _FindAVChannelLayout(int channels) {
+ switch(channels) {
+ case 1: return AV_CH_LAYOUT_MONO;
+ case 2: return AV_CH_LAYOUT_STEREO;
+ case 4: return AV_CH_LAYOUT_QUAD;
+ case 6: return AV_CH_LAYOUT_5POINT1;
+ default: return AV_CH_LAYOUT_STEREO_DOWNMIX;
+ }
+}
+
+void _FindAudioFormat(enum AVSampleFormat fmt, int *bytes, bool *is_signed, unsigned int *format) {
+ switch(fmt) {
+ case AV_SAMPLE_FMT_U8:
+ *bytes = 1;
+ *is_signed = false;
+ *format = AUDIO_U8;
+ break;
+ case AV_SAMPLE_FMT_S16:
+ *bytes = 2;
+ *is_signed = true;
+ *format = AUDIO_S16SYS;
+ break;
+ case AV_SAMPLE_FMT_S32:
+ *bytes = 4;
+ *is_signed = true;
+ *format = AUDIO_S32SYS;
+ break;
+ default:
+ *bytes = 2;
+ *is_signed = true;
+ *format = AUDIO_S16SYS;
+ break;
+ }
+}
+
+void _HandleAudioPacket(Kit_Player *player, AVPacket *packet) {
+ assert(player != NULL);
+ assert(packet != NULL);
+
+ int frame_finished;
+ int len, len2;
+ int dst_linesize;
+ int dst_nb_samples, dst_bufsize;
+ unsigned char **dst_data;
+ AVCodecContext *acodec_ctx = (AVCodecContext*)player->acodec_ctx;
+ AVFormatContext *fmt_ctx = (AVFormatContext *)player->src->format_ctx;
+ struct SwrContext *swr = (struct SwrContext *)player->swr;
+ AVFrame *aframe = (AVFrame*)player->tmp_aframe;
+
+ while(packet->size > 0) {
+ len = avcodec_decode_audio4(acodec_ctx, aframe, &frame_finished, packet);
+ if(len < 0) {
+ return;
+ }
+
+ if(frame_finished) {
+ dst_nb_samples = av_rescale_rnd(
+ aframe->nb_samples,
+ player->aformat.samplerate,
+ acodec_ctx->sample_rate,
+ AV_ROUND_UP);
+
+ av_samples_alloc_array_and_samples(
+ &dst_data,
+ &dst_linesize,
+ player->aformat.channels,
+ dst_nb_samples,
+ _FindAVSampleFormat(player->aformat.format),
+ 0);
+
+ len2 = swr_convert(
+ swr,
+ dst_data,
+ aframe->nb_samples,
+ (const unsigned char **)aframe->extended_data,
+ aframe->nb_samples);
+
+ dst_bufsize = av_samples_get_buffer_size(
+ &dst_linesize,
+ player->aformat.channels,
+ len2,
+ _FindAVSampleFormat(player->aformat.format), 1);
+
+ // Get pts
+ double pts = 0;
+ if(packet->dts != AV_NOPTS_VALUE) {
+ pts = av_frame_get_best_effort_timestamp(player->tmp_aframe);
+ pts *= av_q2d(fmt_ctx->streams[player->src->astream_idx]->time_base);
+ }
+
+ // Just seeked, set sync clock & pos.
+ if(player->seek_flag == 1) {
+ player->vclock_pos = pts;
+ player->clock_sync = _GetSystemTime() - pts;
+ player->seek_flag = 0;
+ }
+
+ // Lock, write to audio buffer, unlock
+ Kit_AudioPacket *apacket = _CreateAudioPacket((char*)dst_data[0], (size_t)dst_bufsize, pts);
+ bool done = false;
+ if(SDL_LockMutex(player->amutex) == 0) {
+ if(Kit_WriteBuffer((Kit_Buffer*)player->abuffer, apacket) == 0) {
+ done = true;
+ }
+ SDL_UnlockMutex(player->amutex);
+ }
+
+ // Couldn't write packet, free memory
+ if(!done) {
+ _FreeAudioPacket(apacket);
+ }
+
+ av_freep(&dst_data[0]);
+ av_freep(&dst_data);
+ }
+
+ packet->size -= len;
+ packet->data += len;
+ }
+}
diff --git a/src/kitcontrol.c b/src/kitcontrol.c
new file mode 100644
index 0000000..0a9a8d8
--- /dev/null
+++ b/src/kitcontrol.c
@@ -0,0 +1,82 @@
+
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <ass/ass.h>
+#include <SDL2/SDL.h>
+
+#include "kitchensink/internal/kitbuffer.h"
+#include "kitchensink/internal/kitlist.h"
+#include "kitchensink/internal/kitcontrol.h"
+
+Kit_ControlPacket* _CreateControlPacket(Kit_ControlPacketType type, double value1) {
+ Kit_ControlPacket *p = calloc(1, sizeof(Kit_ControlPacket));
+ p->type = type;
+ p->value1 = value1;
+ return p;
+}
+
+void _FreeControlPacket(void *ptr) {
+ Kit_ControlPacket *packet = ptr;
+ free(packet);
+}
+
+static int reset_libass_track(Kit_Player *player) {
+ AVCodecContext *scodec_ctx = player->scodec_ctx;
+
+ if(scodec_ctx == NULL) {
+ return 0;
+ }
+
+ // Flush libass track events
+ ass_flush_events(player->ass_track);
+ return 0;
+}
+
+void _HandleFlushCommand(Kit_Player *player, Kit_ControlPacket *packet) {
+ if(player->abuffer != NULL) {
+ if(SDL_LockMutex(player->amutex) == 0) {
+ Kit_ClearBuffer((Kit_Buffer*)player->abuffer);
+ SDL_UnlockMutex(player->amutex);
+ }
+ }
+ if(player->vbuffer != NULL) {
+ if(SDL_LockMutex(player->vmutex) == 0) {
+ Kit_ClearBuffer((Kit_Buffer*)player->vbuffer);
+ SDL_UnlockMutex(player->vmutex);
+ }
+ }
+ if(player->sbuffer != NULL) {
+ if(SDL_LockMutex(player->smutex) == 0) {
+ Kit_ClearList((Kit_List*)player->sbuffer);
+ SDL_UnlockMutex(player->smutex);
+ }
+ }
+ reset_libass_track(player);
+}
+
+void _HandleSeekCommand(Kit_Player *player, Kit_ControlPacket *packet) {
+ AVFormatContext *fmt_ctx = (AVFormatContext *)player->src->format_ctx;
+
+ // Find and limit absolute position
+ double seek = packet->value1;
+ double duration = Kit_GetPlayerDuration(player);
+ if(player->vclock_pos + seek <= 0) {
+ seek = -player->vclock_pos;
+ }
+ if(player->vclock_pos + seek >= duration) {
+ seek = duration - player->vclock_pos;
+ }
+ double absolute_pos = player->vclock_pos + seek;
+ int64_t seek_target = absolute_pos * AV_TIME_BASE;
+
+ // Seek to timestamp.
+ avformat_seek_file(fmt_ctx, -1, INT64_MIN, seek_target, INT64_MAX, 0);
+ if(player->vcodec_ctx != NULL)
+ avcodec_flush_buffers(player->vcodec_ctx);
+ if(player->acodec_ctx != NULL)
+ avcodec_flush_buffers(player->acodec_ctx);
+
+ // On first packet, set clock and current position
+ player->seek_flag = 1;
+}
+
diff --git a/src/kithelpers.c b/src/kithelpers.c
new file mode 100644
index 0000000..9c44d1e
--- /dev/null
+++ b/src/kithelpers.c
@@ -0,0 +1,30 @@
+#include <libavutil/time.h>
+#include <libavutil/avstring.h>
+
+#include "kitchensink/internal/kithelpers.h"
+
+static const char * const font_mime[] = {
+ "application/x-font-ttf",
+ "application/x-font-truetype",
+ "application/x-truetype-font",
+ "application/x-font-opentype",
+ "application/vnd.ms-opentype",
+ "application/font-sfnt",
+ NULL
+};
+
+double _GetSystemTime() {
+ return (double)av_gettime() / 1000000.0;
+}
+
+bool attachment_is_font(AVStream *stream) {
+ AVDictionaryEntry *tag = av_dict_get(stream->metadata, "mimetype", NULL, AV_DICT_MATCH_CASE);
+ if(tag) {
+ for(int n = 0; font_mime[n]; n++) {
+ if(av_strcasecmp(font_mime[n], tag->value) == 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+} \ No newline at end of file
diff --git a/src/kitplayer.c b/src/kitplayer.c
index 9f8a963..480e815 100644
--- a/src/kitplayer.c
+++ b/src/kitplayer.c
@@ -4,16 +4,18 @@
#include "kitchensink/internal/kitringbuffer.h"
#include "kitchensink/internal/kitlist.h"
#include "kitchensink/internal/kitlibstate.h"
+#include "kitchensink/internal/kitvideo.h"
+#include "kitchensink/internal/kitaudio.h"
+#include "kitchensink/internal/kitsubtitle.h"
+#include "kitchensink/internal/kitcontrol.h"
+#include "kitchensink/internal/kithelpers.h"
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libavutil/pixfmt.h>
-#include <libavutil/time.h>
#include <libavutil/samplefmt.h>
-#include <libavutil/avstring.h>
-#include <libavutil/imgutils.h>
#include <SDL2/SDL.h>
#include <ass/ass.h>
@@ -34,41 +36,6 @@
#define VIDEO_SYNC_THRESHOLD 0.01
#define AUDIO_SYNC_THRESHOLD 0.05
-// Buffersizes
-#define KIT_VBUFFERSIZE 3
-#define KIT_ABUFFERSIZE 64
-#define KIT_CBUFFERSIZE 8
-#define KIT_SBUFFERSIZE 512
-
-typedef enum Kit_ControlPacketType {
- KIT_CONTROL_SEEK,
- KIT_CONTROL_FLUSH
-} Kit_ControlPacketType;
-
-typedef struct Kit_VideoPacket {
- double pts;
- AVFrame *frame;
-} Kit_VideoPacket;
-
-typedef struct Kit_AudioPacket {
- double pts;
- size_t original_size;
- Kit_RingBuffer *rb;
-} Kit_AudioPacket;
-
-typedef struct Kit_ControlPacket {
- Kit_ControlPacketType type;
- double value1;
-} Kit_ControlPacket;
-
-typedef struct Kit_SubtitlePacket {
- 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) {
assert(player != NULL);
assert(src != NULL);
@@ -178,525 +145,6 @@ exit_0:
return 1;
}
-static int reset_libass_track(Kit_Player *player) {
- AVCodecContext *scodec_ctx = player->scodec_ctx;
-
- if(scodec_ctx == NULL) {
- return 0;
- }
-
- // Flush libass track events
- ass_flush_events(player->ass_track);
- return 0;
-}
-
-static void _FindPixelFormat(enum AVPixelFormat fmt, unsigned int *out_fmt) {
- switch(fmt) {
- case AV_PIX_FMT_YUV420P9:
- case AV_PIX_FMT_YUV420P10:
- case AV_PIX_FMT_YUV420P12:
- case AV_PIX_FMT_YUV420P14:
- case AV_PIX_FMT_YUV420P16:
- case AV_PIX_FMT_YUV420P:
- *out_fmt = SDL_PIXELFORMAT_YV12;
- break;
- case AV_PIX_FMT_YUYV422:
- *out_fmt = SDL_PIXELFORMAT_YUY2;
- break;
- case AV_PIX_FMT_UYVY422:
- *out_fmt = SDL_PIXELFORMAT_UYVY;
- break;
- default:
- *out_fmt = SDL_PIXELFORMAT_ABGR8888;
- break;
- }
-}
-
-static void _FindAudioFormat(enum AVSampleFormat fmt, int *bytes, bool *is_signed, unsigned int *format) {
- switch(fmt) {
- case AV_SAMPLE_FMT_U8:
- *bytes = 1;
- *is_signed = false;
- *format = AUDIO_U8;
- break;
- case AV_SAMPLE_FMT_S16:
- *bytes = 2;
- *is_signed = true;
- *format = AUDIO_S16SYS;
- break;
- case AV_SAMPLE_FMT_S32:
- *bytes = 4;
- *is_signed = true;
- *format = AUDIO_S32SYS;
- break;
- default:
- *bytes = 2;
- *is_signed = true;
- *format = AUDIO_S16SYS;
- break;
- }
-}
-
-static enum AVPixelFormat _FindAVPixelFormat(unsigned int fmt) {
- switch(fmt) {
- case SDL_PIXELFORMAT_IYUV: return AV_PIX_FMT_YUV420P;
- case SDL_PIXELFORMAT_YV12: return AV_PIX_FMT_YUV420P;
- case SDL_PIXELFORMAT_YUY2: return AV_PIX_FMT_YUYV422;
- case SDL_PIXELFORMAT_UYVY: return AV_PIX_FMT_UYVY422;
- case SDL_PIXELFORMAT_ARGB8888: return AV_PIX_FMT_BGRA;
- case SDL_PIXELFORMAT_ABGR8888: return AV_PIX_FMT_RGBA;
- default:
- return AV_PIX_FMT_NONE;
- }
-}
-
-static enum AVSampleFormat _FindAVSampleFormat(int format) {
- switch(format) {
- case AUDIO_U8: return AV_SAMPLE_FMT_U8;
- case AUDIO_S16SYS: return AV_SAMPLE_FMT_S16;
- case AUDIO_S32SYS: return AV_SAMPLE_FMT_S32;
- default:
- return AV_SAMPLE_FMT_NONE;
- }
-}
-
-static unsigned int _FindAVChannelLayout(int channels) {
- switch(channels) {
- case 1: return AV_CH_LAYOUT_MONO;
- case 2: return AV_CH_LAYOUT_STEREO;
- case 4: return AV_CH_LAYOUT_QUAD;
- case 6: return AV_CH_LAYOUT_5POINT1;
- default: return AV_CH_LAYOUT_STEREO_DOWNMIX;
- }
-}
-
-static Kit_VideoPacket* _CreateVideoPacket(AVFrame *frame, double pts) {
- Kit_VideoPacket *p = calloc(1, sizeof(Kit_VideoPacket));
- p->frame = frame;
- p->pts = pts;
- return p;
-}
-
-static void _FreeVideoPacket(void *ptr) {
- Kit_VideoPacket *packet = ptr;
- av_freep(&packet->frame->data[0]);
- av_frame_free(&packet->frame);
- free(packet);
-}
-
-static Kit_AudioPacket* _CreateAudioPacket(const char* data, size_t len, double pts) {
- Kit_AudioPacket *p = calloc(1, sizeof(Kit_AudioPacket));
- p->rb = Kit_CreateRingBuffer(len);
- Kit_WriteRingBuffer(p->rb, data, len);
- p->pts = pts;
- return p;
-}
-
-static void _FreeAudioPacket(void *ptr) {
- Kit_AudioPacket *packet = ptr;
- Kit_DestroyRingBuffer(packet->rb);
- free(packet);
-}
-
-static Kit_ControlPacket* _CreateControlPacket(Kit_ControlPacketType type, double value1) {
- Kit_ControlPacket *p = calloc(1, sizeof(Kit_ControlPacket));
- p->type = type;
- p->value1 = value1;
- return p;
-}
-
-static void _FreeControlPacket(void *ptr) {
- Kit_ControlPacket *packet = ptr;
- free(packet);
-}
-
-
-static Kit_SubtitlePacket* _CreateSubtitlePacket(double pts_start, double pts_end, SDL_Rect *rect, SDL_Surface *surface) {
- Kit_SubtitlePacket *p = calloc(1, sizeof(Kit_SubtitlePacket));
- 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;
- SDL_FreeSurface(packet->surface);
- if(packet->texture) {
- SDL_DestroyTexture(packet->texture);
- }
- free(packet->rect);
- free(packet);
-}
-
-static double _GetSystemTime() {
- return (double)av_gettime() / 1000000.0;
-}
-
-static void _HandleVideoPacket(Kit_Player *player, AVPacket *packet) {
- assert(player != NULL);
- assert(packet != NULL);
-
- int frame_finished;
- AVCodecContext *vcodec_ctx = (AVCodecContext*)player->vcodec_ctx;
- AVFormatContext *fmt_ctx = (AVFormatContext *)player->src->format_ctx;
- AVFrame *iframe = player->tmp_vframe;
-
- while(packet->size > 0) {
- int len = avcodec_decode_video2(vcodec_ctx, player->tmp_vframe, &frame_finished, packet);
- if(len < 0) {
- return;
- }
-
- if(frame_finished) {
- // Target frame
- AVFrame *oframe = av_frame_alloc();
- av_image_alloc(
- oframe->data,
- oframe->linesize,
- vcodec_ctx->width,
- vcodec_ctx->height,
- _FindAVPixelFormat(player->vformat.format),
- 1);
-
- // Scale from source format to target format, don't touch the size
- sws_scale(
- (struct SwsContext *)player->sws,
- (const unsigned char * const *)iframe->data,
- iframe->linesize,
- 0,
- vcodec_ctx->height,
- oframe->data,
- oframe->linesize);
-
- // Get pts
- double pts = 0;
- if(packet->dts != AV_NOPTS_VALUE) {
- pts = av_frame_get_best_effort_timestamp(player->tmp_vframe);
- pts *= av_q2d(fmt_ctx->streams[player->src->vstream_idx]->time_base);
- }
-
- // Just seeked, set sync clock & pos.
- if(player->seek_flag == 1) {
- player->vclock_pos = pts;
- player->clock_sync = _GetSystemTime() - pts;
- player->seek_flag = 0;
- }
-
- // Lock, write to audio buffer, unlock
- Kit_VideoPacket *vpacket = _CreateVideoPacket(oframe, pts);
- bool done = false;
- if(SDL_LockMutex(player->vmutex) == 0) {
- if(Kit_WriteBuffer((Kit_Buffer*)player->vbuffer, vpacket) == 0) {
- done = true;
- }
- SDL_UnlockMutex(player->vmutex);
- }
-
- // Unable to write packet, free it.
- if(!done) {
- _FreeVideoPacket(vpacket);
- }
- }
- packet->size -= len;
- packet->data += len;
- }
-}
-
-static void _HandleAudioPacket(Kit_Player *player, AVPacket *packet) {
- assert(player != NULL);
- assert(packet != NULL);
-
- int frame_finished;
- int len, len2;
- int dst_linesize;
- int dst_nb_samples, dst_bufsize;
- unsigned char **dst_data;
- AVCodecContext *acodec_ctx = (AVCodecContext*)player->acodec_ctx;
- AVFormatContext *fmt_ctx = (AVFormatContext *)player->src->format_ctx;
- struct SwrContext *swr = (struct SwrContext *)player->swr;
- AVFrame *aframe = (AVFrame*)player->tmp_aframe;
-
- while(packet->size > 0) {
- len = avcodec_decode_audio4(acodec_ctx, aframe, &frame_finished, packet);
- if(len < 0) {
- return;
- }
-
- if(frame_finished) {
- dst_nb_samples = av_rescale_rnd(
- aframe->nb_samples,
- player->aformat.samplerate,
- acodec_ctx->sample_rate,
- AV_ROUND_UP);
-
- av_samples_alloc_array_and_samples(
- &dst_data,
- &dst_linesize,
- player->aformat.channels,
- dst_nb_samples,
- _FindAVSampleFormat(player->aformat.format),
- 0);
-
- len2 = swr_convert(
- swr,
- dst_data,
- aframe->nb_samples,
- (const unsigned char **)aframe->extended_data,
- aframe->nb_samples);
-
- dst_bufsize = av_samples_get_buffer_size(
- &dst_linesize,
- player->aformat.channels,
- len2,
- _FindAVSampleFormat(player->aformat.format), 1);
-
- // Get pts
- double pts = 0;
- if(packet->dts != AV_NOPTS_VALUE) {
- pts = av_frame_get_best_effort_timestamp(player->tmp_aframe);
- pts *= av_q2d(fmt_ctx->streams[player->src->astream_idx]->time_base);
- }
-
- // Just seeked, set sync clock & pos.
- if(player->seek_flag == 1) {
- player->vclock_pos = pts;
- player->clock_sync = _GetSystemTime() - pts;
- player->seek_flag = 0;
- }
-
- // Lock, write to audio buffer, unlock
- Kit_AudioPacket *apacket = _CreateAudioPacket((char*)dst_data[0], (size_t)dst_bufsize, pts);
- bool done = false;
- if(SDL_LockMutex(player->amutex) == 0) {
- if(Kit_WriteBuffer((Kit_Buffer*)player->abuffer, apacket) == 0) {
- done = true;
- }
- SDL_UnlockMutex(player->amutex);
- }
-
- // Couldn't write packet, free memory
- if(!done) {
- _FreeAudioPacket(apacket);
- }
-
- av_freep(&dst_data[0]);
- av_freep(&dst_data);
- }
-
- packet->size -= len;
- packet->data += len;
- }
-}
-
-static void _HandleBitmapSubtitle(Kit_SubtitlePacket** spackets, int *n, 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->data[0],
- rect->w, rect->h, 8,
- rect->linesize[0],
- 0, 0, 0, 0);
-
- SDL_SetPaletteColors(s->format->palette, (SDL_Color*)rect->data[1], 0, 256);
-
- Uint32 rmask, gmask, bmask, amask;
- #if SDL_BYTEORDER == SDL_BIG_ENDIAN
- rmask = 0xff000000;
- gmask = 0x00ff0000;
- bmask = 0x0000ff00;
- amask = 0x000000ff;
- #else
- rmask = 0x000000ff;
- gmask = 0x0000ff00;
- bmask = 0x00ff0000;
- amask = 0xff000000;
- #endif
- SDL_Surface *tmp = SDL_CreateRGBSurface(
- 0, rect->w, rect->h, 32,
- rmask, gmask, bmask, amask);
- 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.0f);
- double end = -1;
- if(sub->end_display_time < UINT_MAX) {
- end = pts + (sub->end_display_time / 1000.0f);
- }
-
- spackets[(*n)++] = _CreateSubtitlePacket(start, end, dst_rect, tmp);
- }
-}
-
-static void _ProcessAssSubtitleRect(Kit_Player *player, AVSubtitleRect *rect) {
- ass_process_data((ASS_Track*)player->ass_track, rect->ass, strlen(rect->ass));
-}
-
-static void _ProcessAssImage(SDL_Surface *surface, const ASS_Image *img) {
- int x, y;
- // libass headers claim img->color is RGBA, but the alpha is 0.
- unsigned char r = ((img->color) >> 24) & 0xFF;
- unsigned char g = ((img->color) >> 16) & 0xFF;
- unsigned char b = ((img->color) >> 8) & 0xFF;
- unsigned char *src = img->bitmap;
- unsigned char *dst = (unsigned char*)surface->pixels;
-
- for(y = 0; y < img->h; y++) {
- for(x = 0; x < img->w; x++) {
- dst[x * 4 + 0] = r;
- dst[x * 4 + 1] = g;
- dst[x * 4 + 2] = b;
- dst[x * 4 + 3] = src[x];
- }
- src += img->stride;
- dst += surface->pitch;
- }
-}
-
-static void _HandleAssSubtitle(Kit_SubtitlePacket** spackets, int *n, Kit_Player *player, double pts, AVSubtitle *sub) {
- double start = pts + (sub->start_display_time / 1000.0f);
- double end = pts + (sub->end_display_time / 1000.0f);
-
- // Process current chunk of data
- unsigned int now = start * 1000;
- int change = 0;
- ASS_Image *images = ass_render_frame((ASS_Renderer*)player->ass_renderer, (ASS_Track*)player->ass_track, now, &change);
-
- // Convert to SDL_Surfaces
- if(change > 0) {
- ASS_Image *now = images;
- if(now != NULL) {
- do {
- Uint32 rmask, gmask, bmask, amask;
- #if SDL_BYTEORDER == SDL_BIG_ENDIAN
- rmask = 0xff000000;
- gmask = 0x00ff0000;
- bmask = 0x0000ff00;
- amask = 0x000000ff;
- #else
- rmask = 0x000000ff;
- gmask = 0x0000ff00;
- bmask = 0x00ff0000;
- amask = 0xff000000;
- #endif
- SDL_Surface *tmp = SDL_CreateRGBSurface(
- 0, now->w, now->h, 32,
- rmask, gmask, bmask, amask);
-
- _ProcessAssImage(tmp, now);
-
- SDL_Rect *dst_rect = malloc(sizeof(SDL_Rect));
- dst_rect->x = now->dst_x;
- dst_rect->y = now->dst_y;
- dst_rect->w = now->w;
- dst_rect->h = now->h;
-
- spackets[(*n)++] = _CreateSubtitlePacket(start, end, dst_rect, tmp);
- } while((now = now->next) != NULL);
- }
- }
-}
-
-static void _HandleSubtitlePacket(Kit_Player *player, AVPacket *packet) {
- assert(player != NULL);
- assert(packet != NULL);
-
- int frame_finished;
- int len;
- AVCodecContext *scodec_ctx = (AVCodecContext*)player->scodec_ctx;
- AVFormatContext *fmt_ctx = (AVFormatContext *)player->src->format_ctx;
- Kit_SubtitlePacket *tmp = NULL;
- unsigned int it;
- AVSubtitle sub;
- memset(&sub, 0, sizeof(AVSubtitle));
-
- if(packet->size > 0) {
- len = avcodec_decode_subtitle2(scodec_ctx, &sub, &frame_finished, packet);
- if(len < 0) {
- return;
- }
-
- if(frame_finished) {
- // Get pts
- double pts = 0;
- if(packet->dts != AV_NOPTS_VALUE) {
- pts = packet->pts;
- pts *= av_q2d(fmt_ctx->streams[player->src->sstream_idx]->time_base);
- }
-
- // Convert subtitles to SDL_Surface and create a packet
- Kit_SubtitlePacket *spackets[KIT_SBUFFERSIZE];
- memset(spackets, 0, sizeof(Kit_SubtitlePacket*) * KIT_SBUFFERSIZE);
-
- int n = 0;
- bool has_ass = false;
- for(int r = 0; r < sub.num_rects; r++) {
- switch(sub.rects[r]->type) {
- case SUBTITLE_BITMAP:
- _HandleBitmapSubtitle(spackets, &n, player, pts, &sub, sub.rects[r]);
- break;
- case SUBTITLE_ASS:
- _ProcessAssSubtitleRect(player, sub.rects[r]);
- has_ass = true;
- break;
- case SUBTITLE_TEXT:
- break;
- case SUBTITLE_NONE:
- break;
- }
- }
-
- // Process libass content
- if(has_ass) {
- _HandleAssSubtitle(spackets, &n, player, pts, &sub);
- }
-
- // Lock, write to subtitle buffer, unlock
- if(SDL_LockMutex(player->smutex) == 0) {
- if(has_ass) {
- Kit_ClearList((Kit_List*)player->sbuffer);
- } else {
- // Clear out old subtitles that should only be valid until next (this) subtitle
- it = 0;
- while((tmp = Kit_IterateList((Kit_List*)player->sbuffer, &it)) != NULL) {
- if(tmp->pts_end < 0) {
- Kit_RemoveFromList((Kit_List*)player->sbuffer, it);
- }
- }
- }
-
- // Add new subtitle
- for(int i = 0; i < KIT_SBUFFERSIZE; i++) {
- Kit_SubtitlePacket *spacket = spackets[i];
- if(spacket != NULL) {
- if(Kit_WriteList((Kit_List*)player->sbuffer, spacket) == 0) {
- spackets[i] = NULL;
- }
- }
- }
-
- // Unlock subtitle buffer
- SDL_UnlockMutex(player->smutex);
- }
-
- // Couldn't write packet, free memory
- for(int i = 0; i < KIT_SBUFFERSIZE; i++) {
- if(spackets[i] != NULL) {
- _FreeSubtitlePacket(spackets[i]);
- }
- }
- }
- }
-}
-
static void _HandlePacket(Kit_Player *player, AVPacket *packet) {
// Check if this is a packet we need to handle and pass it on
if(player->vcodec_ctx != NULL && packet->stream_index == player->src->vstream_idx) {
@@ -710,54 +158,6 @@ static void _HandlePacket(Kit_Player *player, AVPacket *packet) {
}
}
-static void _HandleFlushCommand(Kit_Player *player, Kit_ControlPacket *packet) {
- if(player->abuffer != NULL) {
- if(SDL_LockMutex(player->amutex) == 0) {
- Kit_ClearBuffer((Kit_Buffer*)player->abuffer);
- SDL_UnlockMutex(player->amutex);
- }
- }
- if(player->vbuffer != NULL) {
- if(SDL_LockMutex(player->vmutex) == 0) {
- Kit_ClearBuffer((Kit_Buffer*)player->vbuffer);
- SDL_UnlockMutex(player->vmutex);
- }
- }
- if(player->sbuffer != NULL) {
- if(SDL_LockMutex(player->smutex) == 0) {
- Kit_ClearList((Kit_List*)player->sbuffer);
- SDL_UnlockMutex(player->smutex);
- }
- }
- reset_libass_track(player);
-}
-
-static void _HandleSeekCommand(Kit_Player *player, Kit_ControlPacket *packet) {
- AVFormatContext *fmt_ctx = (AVFormatContext *)player->src->format_ctx;
-
- // Find and limit absolute position
- double seek = packet->value1;
- double duration = Kit_GetPlayerDuration(player);
- if(player->vclock_pos + seek <= 0) {
- seek = -player->vclock_pos;
- }
- if(player->vclock_pos + seek >= duration) {
- seek = duration - player->vclock_pos;
- }
- double absolute_pos = player->vclock_pos + seek;
- int64_t seek_target = absolute_pos * AV_TIME_BASE;
-
- // Seek to timestamp.
- avformat_seek_file(fmt_ctx, -1, INT64_MIN, seek_target, INT64_MAX, 0);
- if(player->vcodec_ctx != NULL)
- avcodec_flush_buffers(player->vcodec_ctx);
- if(player->acodec_ctx != NULL)
- avcodec_flush_buffers(player->acodec_ctx);
-
- // On first packet, set clock and current position
- player->seek_flag = 1;
-}
-
static void _HandleControlPacket(Kit_Player *player, Kit_ControlPacket *packet) {
switch(packet->type) {
case KIT_CONTROL_FLUSH:
@@ -859,28 +259,6 @@ static int _DecoderThread(void *ptr) {
return 0;
}
-static const char * const font_mime[] = {
- "application/x-font-ttf",
- "application/x-font-truetype",
- "application/x-truetype-font",
- "application/x-font-opentype",
- "application/vnd.ms-opentype",
- "application/font-sfnt",
- NULL
-};
-
-static bool attachment_is_font(AVStream *stream) {
- AVDictionaryEntry *tag = av_dict_get(stream->metadata, "mimetype", NULL, AV_DICT_MATCH_CASE);
- if(tag) {
- for(int n = 0; font_mime[n]; n++) {
- if(av_strcasecmp(font_mime[n], tag->value) == 0) {
- return true;
- }
- }
- }
- return false;
-}
-
Kit_Player* Kit_CreatePlayer(const Kit_Source *src) {
assert(src != NULL);
diff --git a/src/kitsubtitle.c b/src/kitsubtitle.c
new file mode 100644
index 0000000..82a48c2
--- /dev/null
+++ b/src/kitsubtitle.c
@@ -0,0 +1,234 @@
+#include <assert.h>
+
+#include <SDL2/SDL.h>
+#include <ass/ass.h>
+
+#include "kitchensink/internal/kitsubtitle.h"
+#include "kitchensink/internal/kitlist.h"
+
+
+Kit_SubtitlePacket* _CreateSubtitlePacket(double pts_start, double pts_end, SDL_Rect *rect, SDL_Surface *surface) {
+ Kit_SubtitlePacket *p = calloc(1, sizeof(Kit_SubtitlePacket));
+ p->pts_start = pts_start;
+ p->pts_end = pts_end;
+ p->surface = surface;
+ p->rect = rect;
+ p->texture = NULL; // Cached texture
+ return p;
+}
+
+void _FreeSubtitlePacket(void *ptr) {
+ Kit_SubtitlePacket *packet = ptr;
+ SDL_FreeSurface(packet->surface);
+ if(packet->texture) {
+ SDL_DestroyTexture(packet->texture);
+ }
+ free(packet->rect);
+ free(packet);
+}
+
+void _HandleBitmapSubtitle(Kit_SubtitlePacket** spackets, int *n, 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->data[0],
+ rect->w, rect->h, 8,
+ rect->linesize[0],
+ 0, 0, 0, 0);
+
+ SDL_SetPaletteColors(s->format->palette, (SDL_Color*)rect->data[1], 0, 256);
+
+ Uint32 rmask, gmask, bmask, amask;
+ #if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ rmask = 0xff000000;
+ gmask = 0x00ff0000;
+ bmask = 0x0000ff00;
+ amask = 0x000000ff;
+ #else
+ rmask = 0x000000ff;
+ gmask = 0x0000ff00;
+ bmask = 0x00ff0000;
+ amask = 0xff000000;
+ #endif
+ SDL_Surface *tmp = SDL_CreateRGBSurface(
+ 0, rect->w, rect->h, 32,
+ rmask, gmask, bmask, amask);
+ 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.0f);
+ double end = -1;
+ if(sub->end_display_time < UINT_MAX) {
+ end = pts + (sub->end_display_time / 1000.0f);
+ }
+
+ spackets[(*n)++] = _CreateSubtitlePacket(start, end, dst_rect, tmp);
+ }
+}
+
+static void _ProcessAssSubtitleRect(Kit_Player *player, AVSubtitleRect *rect) {
+ ass_process_data((ASS_Track*)player->ass_track, rect->ass, strlen(rect->ass));
+}
+
+static void _ProcessAssImage(SDL_Surface *surface, const ASS_Image *img) {
+ int x, y;
+ // libass headers claim img->color is RGBA, but the alpha is 0.
+ unsigned char r = ((img->color) >> 24) & 0xFF;
+ unsigned char g = ((img->color) >> 16) & 0xFF;
+ unsigned char b = ((img->color) >> 8) & 0xFF;
+ unsigned char *src = img->bitmap;
+ unsigned char *dst = (unsigned char*)surface->pixels;
+
+ for(y = 0; y < img->h; y++) {
+ for(x = 0; x < img->w; x++) {
+ dst[x * 4 + 0] = r;
+ dst[x * 4 + 1] = g;
+ dst[x * 4 + 2] = b;
+ dst[x * 4 + 3] = src[x];
+ }
+ src += img->stride;
+ dst += surface->pitch;
+ }
+}
+
+static void _HandleAssSubtitle(Kit_SubtitlePacket** spackets, int *n, Kit_Player *player, double pts, AVSubtitle *sub) {
+ double start = pts + (sub->start_display_time / 1000.0f);
+ double end = pts + (sub->end_display_time / 1000.0f);
+
+ // Process current chunk of data
+ unsigned int now = start * 1000;
+ int change = 0;
+ ASS_Image *images = ass_render_frame((ASS_Renderer*)player->ass_renderer, (ASS_Track*)player->ass_track, now, &change);
+
+ // Convert to SDL_Surfaces
+ if(change > 0) {
+ ASS_Image *now = images;
+ if(now != NULL) {
+ do {
+ Uint32 rmask, gmask, bmask, amask;
+ #if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ rmask = 0xff000000;
+ gmask = 0x00ff0000;
+ bmask = 0x0000ff00;
+ amask = 0x000000ff;
+ #else
+ rmask = 0x000000ff;
+ gmask = 0x0000ff00;
+ bmask = 0x00ff0000;
+ amask = 0xff000000;
+ #endif
+ SDL_Surface *tmp = SDL_CreateRGBSurface(
+ 0, now->w, now->h, 32,
+ rmask, gmask, bmask, amask);
+
+ _ProcessAssImage(tmp, now);
+
+ SDL_Rect *dst_rect = malloc(sizeof(SDL_Rect));
+ dst_rect->x = now->dst_x;
+ dst_rect->y = now->dst_y;
+ dst_rect->w = now->w;
+ dst_rect->h = now->h;
+
+ spackets[(*n)++] = _CreateSubtitlePacket(start, end, dst_rect, tmp);
+ } while((now = now->next) != NULL);
+ }
+ }
+}
+
+void _HandleSubtitlePacket(Kit_Player *player, AVPacket *packet) {
+ assert(player != NULL);
+ assert(packet != NULL);
+
+ int frame_finished;
+ int len;
+ AVCodecContext *scodec_ctx = (AVCodecContext*)player->scodec_ctx;
+ AVFormatContext *fmt_ctx = (AVFormatContext *)player->src->format_ctx;
+ Kit_SubtitlePacket *tmp = NULL;
+ unsigned int it;
+ AVSubtitle sub;
+ memset(&sub, 0, sizeof(AVSubtitle));
+
+ if(packet->size > 0) {
+ len = avcodec_decode_subtitle2(scodec_ctx, &sub, &frame_finished, packet);
+ if(len < 0) {
+ return;
+ }
+
+ if(frame_finished) {
+ // Get pts
+ double pts = 0;
+ if(packet->dts != AV_NOPTS_VALUE) {
+ pts = packet->pts;
+ pts *= av_q2d(fmt_ctx->streams[player->src->sstream_idx]->time_base);
+ }
+
+ // Convert subtitles to SDL_Surface and create a packet
+ Kit_SubtitlePacket *spackets[KIT_SBUFFERSIZE];
+ memset(spackets, 0, sizeof(Kit_SubtitlePacket*) * KIT_SBUFFERSIZE);
+
+ int n = 0;
+ bool has_ass = false;
+ for(int r = 0; r < sub.num_rects; r++) {
+ switch(sub.rects[r]->type) {
+ case SUBTITLE_BITMAP:
+ _HandleBitmapSubtitle(spackets, &n, player, pts, &sub, sub.rects[r]);
+ break;
+ case SUBTITLE_ASS:
+ _ProcessAssSubtitleRect(player, sub.rects[r]);
+ has_ass = true;
+ break;
+ case SUBTITLE_TEXT:
+ break;
+ case SUBTITLE_NONE:
+ break;
+ }
+ }
+
+ // Process libass content
+ if(has_ass) {
+ _HandleAssSubtitle(spackets, &n, player, pts, &sub);
+ }
+
+ // Lock, write to subtitle buffer, unlock
+ if(SDL_LockMutex(player->smutex) == 0) {
+ if(has_ass) {
+ Kit_ClearList((Kit_List*)player->sbuffer);
+ } else {
+ // Clear out old subtitles that should only be valid until next (this) subtitle
+ it = 0;
+ while((tmp = Kit_IterateList((Kit_List*)player->sbuffer, &it)) != NULL) {
+ if(tmp->pts_end < 0) {
+ Kit_RemoveFromList((Kit_List*)player->sbuffer, it);
+ }
+ }
+ }
+
+ // Add new subtitle
+ for(int i = 0; i < KIT_SBUFFERSIZE; i++) {
+ Kit_SubtitlePacket *spacket = spackets[i];
+ if(spacket != NULL) {
+ if(Kit_WriteList((Kit_List*)player->sbuffer, spacket) == 0) {
+ spackets[i] = NULL;
+ }
+ }
+ }
+
+ // Unlock subtitle buffer
+ SDL_UnlockMutex(player->smutex);
+ }
+
+ // Couldn't write packet, free memory
+ for(int i = 0; i < KIT_SBUFFERSIZE; i++) {
+ if(spackets[i] != NULL) {
+ _FreeSubtitlePacket(spackets[i]);
+ }
+ }
+ }
+ }
+}
diff --git a/src/kitvideo.c b/src/kitvideo.c
new file mode 100644
index 0000000..d68b835
--- /dev/null
+++ b/src/kitvideo.c
@@ -0,0 +1,128 @@
+#include <assert.h>
+
+#include <libavutil/imgutils.h>
+#include <libswscale/swscale.h>
+
+#include "kitchensink/internal/kitvideo.h"
+#include "kitchensink/internal/kithelpers.h"
+#include "kitchensink/internal/kitbuffer.h"
+
+
+Kit_VideoPacket* _CreateVideoPacket(AVFrame *frame, double pts) {
+ Kit_VideoPacket *p = calloc(1, sizeof(Kit_VideoPacket));
+ p->frame = frame;
+ p->pts = pts;
+ return p;
+}
+
+void _FreeVideoPacket(void *ptr) {
+ Kit_VideoPacket *packet = ptr;
+ av_freep(&packet->frame->data[0]);
+ av_frame_free(&packet->frame);
+ free(packet);
+}
+
+void _FindPixelFormat(enum AVPixelFormat fmt, unsigned int *out_fmt) {
+ switch(fmt) {
+ case AV_PIX_FMT_YUV420P9:
+ case AV_PIX_FMT_YUV420P10:
+ case AV_PIX_FMT_YUV420P12:
+ case AV_PIX_FMT_YUV420P14:
+ case AV_PIX_FMT_YUV420P16:
+ case AV_PIX_FMT_YUV420P:
+ *out_fmt = SDL_PIXELFORMAT_YV12;
+ break;
+ case AV_PIX_FMT_YUYV422:
+ *out_fmt = SDL_PIXELFORMAT_YUY2;
+ break;
+ case AV_PIX_FMT_UYVY422:
+ *out_fmt = SDL_PIXELFORMAT_UYVY;
+ break;
+ default:
+ *out_fmt = SDL_PIXELFORMAT_ABGR8888;
+ break;
+ }
+}
+
+enum AVPixelFormat _FindAVPixelFormat(unsigned int fmt) {
+ switch(fmt) {
+ case SDL_PIXELFORMAT_IYUV: return AV_PIX_FMT_YUV420P;
+ case SDL_PIXELFORMAT_YV12: return AV_PIX_FMT_YUV420P;
+ case SDL_PIXELFORMAT_YUY2: return AV_PIX_FMT_YUYV422;
+ case SDL_PIXELFORMAT_UYVY: return AV_PIX_FMT_UYVY422;
+ case SDL_PIXELFORMAT_ARGB8888: return AV_PIX_FMT_BGRA;
+ case SDL_PIXELFORMAT_ABGR8888: return AV_PIX_FMT_RGBA;
+ default:
+ return AV_PIX_FMT_NONE;
+ }
+}
+
+void _HandleVideoPacket(Kit_Player *player, AVPacket *packet) {
+ assert(player != NULL);
+ assert(packet != NULL);
+
+ int frame_finished;
+ AVCodecContext *vcodec_ctx = (AVCodecContext*)player->vcodec_ctx;
+ AVFormatContext *fmt_ctx = (AVFormatContext *)player->src->format_ctx;
+ AVFrame *iframe = player->tmp_vframe;
+
+ while(packet->size > 0) {
+ int len = avcodec_decode_video2(vcodec_ctx, player->tmp_vframe, &frame_finished, packet);
+ if(len < 0) {
+ return;
+ }
+
+ if(frame_finished) {
+ // Target frame
+ AVFrame *oframe = av_frame_alloc();
+ av_image_alloc(
+ oframe->data,
+ oframe->linesize,
+ vcodec_ctx->width,
+ vcodec_ctx->height,
+ _FindAVPixelFormat(player->vformat.format),
+ 1);
+
+ // Scale from source format to target format, don't touch the size
+ sws_scale(
+ (struct SwsContext *)player->sws,
+ (const unsigned char * const *)iframe->data,
+ iframe->linesize,
+ 0,
+ vcodec_ctx->height,
+ oframe->data,
+ oframe->linesize);
+
+ // Get pts
+ double pts = 0;
+ if(packet->dts != AV_NOPTS_VALUE) {
+ pts = av_frame_get_best_effort_timestamp(player->tmp_vframe);
+ pts *= av_q2d(fmt_ctx->streams[player->src->vstream_idx]->time_base);
+ }
+
+ // Just seeked, set sync clock & pos.
+ if(player->seek_flag == 1) {
+ player->vclock_pos = pts;
+ player->clock_sync = _GetSystemTime() - pts;
+ player->seek_flag = 0;
+ }
+
+ // Lock, write to audio buffer, unlock
+ Kit_VideoPacket *vpacket = _CreateVideoPacket(oframe, pts);
+ bool done = false;
+ if(SDL_LockMutex(player->vmutex) == 0) {
+ if(Kit_WriteBuffer((Kit_Buffer*)player->vbuffer, vpacket) == 0) {
+ done = true;
+ }
+ SDL_UnlockMutex(player->vmutex);
+ }
+
+ // Unable to write packet, free it.
+ if(!done) {
+ _FreeVideoPacket(vpacket);
+ }
+ }
+ packet->size -= len;
+ packet->data += len;
+ }
+}