diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | include/kitchensink/internal/kitaudio.h | 28 | ||||
-rw-r--r-- | include/kitchensink/internal/kitcontrol.h | 24 | ||||
-rw-r--r-- | include/kitchensink/internal/kithelpers.h | 11 | ||||
-rw-r--r-- | include/kitchensink/internal/kitsubtitle.h | 24 | ||||
-rw-r--r-- | include/kitchensink/internal/kitvideo.h | 22 | ||||
-rw-r--r-- | include/kitchensink/kitchensink.h | 1 | ||||
-rw-r--r-- | include/kitchensink/kitformats.h | 29 | ||||
-rw-r--r-- | include/kitchensink/kitplayer.h | 24 | ||||
-rw-r--r-- | src/kitaudio.c | 155 | ||||
-rw-r--r-- | src/kitcontrol.c | 82 | ||||
-rw-r--r-- | src/kithelpers.c | 30 | ||||
-rw-r--r-- | src/kitplayer.c | 632 | ||||
-rw-r--r-- | src/kitsubtitle.c | 234 | ||||
-rw-r--r-- | src/kitvideo.c | 128 |
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;
+ }
+}
|