summaryrefslogtreecommitdiff
path: root/src/kitplayer.c
diff options
context:
space:
mode:
authorTuomas Virtanen <katajakasa@gmail.com>2017-09-12 00:06:31 +0300
committerTuomas Virtanen <katajakasa@gmail.com>2017-09-22 15:15:57 +0300
commitfd7bbceb6321250ed5d80103d13e9d7584b552ac (patch)
treeb8202cd4c110d3e93c1c7a4032252e66c3e11073 /src/kitplayer.c
parentbf1d1e30ac4062a2f764193a865761161ec7fc6b (diff)
Split kitplayer.c to multiple files
Diffstat (limited to 'src/kitplayer.c')
-rw-r--r--src/kitplayer.c632
1 files changed, 5 insertions, 627 deletions
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);