summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTuomas Virtanen <katajakasa@gmail.com>2017-09-22 15:14:58 +0300
committerTuomas Virtanen <katajakasa@gmail.com>2017-09-22 15:17:21 +0300
commitfdaaf05142e446f1047487763585c3a16801580c (patch)
tree8042ce6b2464a2bce4300c6a7b6a841209f24c6a
parentfd7bbceb6321250ed5d80103d13e9d7584b552ac (diff)
Split decoding to separate files
-rw-r--r--CMakeLists.txt41
-rw-r--r--examples/example_video.c26
-rw-r--r--include/kitchensink/internal/kitaudio.h22
-rw-r--r--include/kitchensink/internal/kitcontrol.h24
-rw-r--r--include/kitchensink/internal/kitdecoder.h54
-rw-r--r--include/kitchensink/internal/kitlog.h11
-rw-r--r--include/kitchensink/internal/kitsubtitle.h20
-rw-r--r--include/kitchensink/internal/kitvideo.h18
-rw-r--r--include/kitchensink/kitformats.h6
-rw-r--r--include/kitchensink/kitplayer.h51
-rw-r--r--include/kitchensink/kitsource.h8
-rw-r--r--src/kitaudio.c247
-rw-r--r--src/kitcontrol.c82
-rw-r--r--src/kitdecoder.c220
-rw-r--r--src/kitlib.c6
-rw-r--r--src/kitplayer.c825
-rw-r--r--src/kitsource.c18
-rw-r--r--src/kitsubtitle.c234
-rw-r--r--src/kitvideo.c258
19 files changed, 1142 insertions, 1029 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 16ddf7c..971b039 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,22 +20,32 @@ set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} -Os -DNDEBUG")
option(BUILD_EXAMPLES "Build examples" OFF)
option(BUILD_TESTS "Build unittests" OFF)
+option(USE_ASS "Use libass" ON)
find_package(SDL2)
-find_package(ass)
find_package(ffmpeg COMPONENTS avcodec avformat avutil swscale swresample)
-if(BUILD_TESTS)
- add_subdirectory(tests)
-endif()
-
-include_directories(
+set(LIBRARIES
+ ${SDL2_LIBRARIES}
+ ${FFMPEG_LIBRARIES}
+)
+set(INCLUDES
include/
${SDL2_INCLUDE_DIRS}
${FFMPEG_INCLUDE_DIRS}
- ${ASS_INCLUDE_DIRS}
)
+if(BUILD_TESTS)
+ add_subdirectory(tests)
+endif()
+
+if(USE_ASS)
+ find_package(ass)
+ add_definitions(-DUSE_ASS)
+ set(LIBRARIES ${LIBRARIES} ${ASS_LIBRARIES})
+ set(INCLUDES ${INCLUDES} ${ASS_INCLUDE_DIRS})
+endif()
+
FILE(GLOB SOURCES "src/*.c")
FILE(GLOB HEADERS "include/kitchensink/*.h")
@@ -44,16 +54,15 @@ add_library(SDL_kitchensink_static STATIC ${SOURCES})
set_target_properties(SDL_kitchensink PROPERTIES VERSION ${KIT_VERSION})
set_target_properties(SDL_kitchensink PROPERTIES SOVERSION ${KIT_VERSION_MAJOR})
+
set_target_properties(SDL_kitchensink PROPERTIES DEBUG_POSTFIX "d")
set_target_properties(SDL_kitchensink_static PROPERTIES DEBUG_POSTFIX "d")
+
target_compile_definitions(SDL_kitchensink PRIVATE "KIT_DLL;KIT_DLL_EXPORTS")
target_compile_options(SDL_kitchensink PRIVATE "-fvisibility=hidden")
-target_link_libraries(SDL_kitchensink
- ${SDL2_LIBRARIES}
- ${FFMPEG_LIBRARIES}
- ${ASS_LIBRARIES}
-)
+include_directories(${INCLUDES})
+target_link_libraries(SDL_kitchensink ${LIBRARIES})
if(BUILD_EXAMPLES)
add_executable(exampleaudio examples/example_audio.c)
@@ -66,15 +75,11 @@ if(BUILD_EXAMPLES)
target_link_libraries(exampleaudio
SDL_kitchensink_static
- ${SDL2_LIBRARIES}
- ${FFMPEG_LIBRARIES}
- ${ASS_LIBRARIES}
+ ${LIBRARIES}
)
target_link_libraries(examplevideo
SDL_kitchensink_static
- ${SDL2_LIBRARIES}
- ${FFMPEG_LIBRARIES}
- ${ASS_LIBRARIES}
+ ${LIBRARIES}
)
endif()
diff --git a/examples/example_video.c b/examples/example_video.c
index 1f411ca..15186fe 100644
--- a/examples/example_video.c
+++ b/examples/example_video.c
@@ -222,30 +222,6 @@ int main(int argc, char *argv[]) {
if(event.key.keysym.sym == SDLK_ESCAPE) {
run = false;
}
- if(event.key.keysym.sym == SDLK_q) {
- // Start or unpause the video
- Kit_PlayerPlay(player);
- }
- if(event.key.keysym.sym == SDLK_w) {
- // Pause playback
- Kit_PlayerPause(player);
- }
- if(event.key.keysym.sym == SDLK_e) {
- // Stop playback (will close the window)
- Kit_PlayerStop(player);
- }
- if(event.key.keysym.sym == SDLK_RIGHT) {
- // Skip 10 seconds forwards or to the end of the file
- if(Kit_PlayerSeek(player, 10.0) != 0) {
- fprintf(stderr, "%s\n", Kit_GetError());
- }
- }
- if(event.key.keysym.sym == SDLK_LEFT) {
- // Seek 10 seconds backwards or to the start of the file
- if(Kit_PlayerSeek(player, -10.0) != 0) {
- fprintf(stderr, "%s\n", Kit_GetError());
- }
- }
break;
case SDL_KEYDOWN:
@@ -270,7 +246,7 @@ int main(int argc, char *argv[]) {
// Handle user clicking the progress bar
if(mouse_x >= 30 && mouse_x <= size_w-30 && mouse_y >= size_h - 60 && mouse_y <= size_h - 40) {
double pos = ((double)mouse_x - 30) / ((double)size_w - 60);
- double m_time = Kit_GetPlayerDuration(player) * pos - Kit_GetPlayerPosition(player);
+ double m_time = Kit_GetPlayerDuration(player) * pos;
if(Kit_PlayerSeek(player, m_time) != 0) {
fprintf(stderr, "%s\n", Kit_GetError());
}
diff --git a/include/kitchensink/internal/kitaudio.h b/include/kitchensink/internal/kitaudio.h
index e1c9ffb..ac6753b 100644
--- a/include/kitchensink/internal/kitaudio.h
+++ b/include/kitchensink/internal/kitaudio.h
@@ -1,28 +1,12 @@
#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;
+#include "kitchensink/internal/kitdecoder.h"
-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);
+KIT_LOCAL Kit_Decoder* Kit_CreateAudioDecoder(const Kit_Source *src, Kit_AudioFormat *format);
+KIT_LOCAL int Kit_GetAudioDecoderData(Kit_Decoder *dec, unsigned char *buf, int len);
#endif // KITAUDIO_H
diff --git a/include/kitchensink/internal/kitcontrol.h b/include/kitchensink/internal/kitcontrol.h
deleted file mode 100644
index eaad484..0000000
--- a/include/kitchensink/internal/kitcontrol.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#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/kitdecoder.h b/include/kitchensink/internal/kitdecoder.h
new file mode 100644
index 0000000..9d6f84d
--- /dev/null
+++ b/include/kitchensink/internal/kitdecoder.h
@@ -0,0 +1,54 @@
+#ifndef KITDECODER_H
+#define KITDECODER_H
+
+#include <stdbool.h>
+
+#include <SDL2/SDL_mutex.h>
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+
+#include "kitchensink/kitconfig.h"
+#include "kitchensink/kitsource.h"
+#include "kitchensink/internal/kitbuffer.h"
+
+#define KIT_DEC_IN 0
+#define KIT_DEC_OUT 1
+
+typedef struct Kit_Decoder Kit_Decoder;
+
+typedef int (*dec_decode_cb)(Kit_Decoder *dec, AVPacket *in_packet);
+typedef void (*dec_close_cb)(Kit_Decoder *dec);
+typedef void (*dec_free_packet_cb)(void *packet);
+
+KIT_LOCAL struct Kit_Decoder {
+ int stream_index; ///< Source stream index for the current stream
+ double clock_sync; ///< Sync source for current stream
+ double clock_pos; ///< Current pts for the stream
+
+ AVCodecContext *codec_ctx; ///< FFMpeg internal: Codec context
+ AVFormatContext *format_ctx; ///< FFMpeg internal: Format context (owner: Kit_Source)
+
+ SDL_mutex *lock[2]; ///< Threading locks for input and output buffers
+ Kit_Buffer *buffer[2]; ///< Buffers for incoming and decoded packets
+
+ void *userdata; ///< Decoder specific information (Audio, video, subtitle context)
+ dec_decode_cb dec_decode; ///< Decoder decoding function callback
+ dec_close_cb dec_close; ///< Decoder close function callback
+};
+
+KIT_LOCAL Kit_Decoder* Kit_CreateDecoder(const Kit_Source *src, int stream_index,
+ int in_b_size, int out_b_size,
+ dec_free_packet_cb free_out_cb);
+KIT_LOCAL void Kit_SetDecoderClockSync(Kit_Decoder *dec, double sync);
+KIT_LOCAL void Kit_ChangeDecoderClockSync(Kit_Decoder *dec, double sync);
+KIT_LOCAL bool Kit_CanWriteDecoderInput(Kit_Decoder *dec);
+KIT_LOCAL int Kit_WriteDecoderInput(Kit_Decoder *dec, AVPacket *packet);
+KIT_LOCAL AVPacket* Kit_ReadDecoderInput(Kit_Decoder *dec);
+KIT_LOCAL int Kit_WriteDecoderOutput(Kit_Decoder *dec, void *packet);
+KIT_LOCAL void* Kit_PeekDecoderOutput(Kit_Decoder *dec);
+KIT_LOCAL void Kit_AdvanceDecoderOutput(Kit_Decoder *dec);
+KIT_LOCAL void Kit_ClearDecoderBuffers(Kit_Decoder *dec);
+KIT_LOCAL int Kit_RunDecoder(Kit_Decoder *dec);
+KIT_LOCAL void Kit_CloseDecoder(Kit_Decoder *dec);
+
+#endif // KITDECODER_H
diff --git a/include/kitchensink/internal/kitlog.h b/include/kitchensink/internal/kitlog.h
new file mode 100644
index 0000000..1a56e53
--- /dev/null
+++ b/include/kitchensink/internal/kitlog.h
@@ -0,0 +1,11 @@
+#ifndef KITLOG_H
+#define KITLOG_H
+
+#ifdef NDEBUG
+ #define LOG(...)
+#else
+ #include <stdio.h>
+ #define LOG(...) fprintf(stderr, __VA_ARGS__)
+#endif
+
+#endif // KITLOG_H
diff --git a/include/kitchensink/internal/kitsubtitle.h b/include/kitchensink/internal/kitsubtitle.h
index 4582744..5296aef 100644
--- a/include/kitchensink/internal/kitsubtitle.h
+++ b/include/kitchensink/internal/kitsubtitle.h
@@ -1,24 +1,14 @@
#ifndef KITSUBTITLE_H
#define KITSUBTITLE_H
-#include <libavformat/avformat.h>
+#include <SDL2/SDL_render.h>
#include "kitchensink/kitconfig.h"
+#include "kitchensink/kitformats.h"
#include "kitchensink/kitplayer.h"
+#include "kitchensink/internal/kitdecoder.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);
+KIT_LOCAL Kit_Decoder* Kit_CreateSubtitleDecoder(const Kit_Source *src, Kit_SubtitleFormat *format, int w, int h);
+KIT_LOCAL int Kit_GetSubtitleDecoderData(Kit_Decoder *dec, SDL_Renderer *renderer);
#endif // KITSUBTITLE_H
diff --git a/include/kitchensink/internal/kitvideo.h b/include/kitchensink/internal/kitvideo.h
index f1cb4ff..5a5624f 100644
--- a/include/kitchensink/internal/kitvideo.h
+++ b/include/kitchensink/internal/kitvideo.h
@@ -1,22 +1,12 @@
#ifndef KITVIDEO_H
#define KITVIDEO_H
-#include <libavformat/avformat.h>
-
#include "kitchensink/kitconfig.h"
+#include "kitchensink/kitformats.h"
#include "kitchensink/kitplayer.h"
+#include "kitchensink/internal/kitdecoder.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);
+KIT_LOCAL Kit_Decoder* Kit_CreateVideoDecoder(const Kit_Source *src, Kit_VideoFormat *format);
+KIT_LOCAL int Kit_GetVideoDecoderData(Kit_Decoder *dec, SDL_Texture *texture);
#endif // KITVIDEO_H
diff --git a/include/kitchensink/kitformats.h b/include/kitchensink/kitformats.h
index ef86826..a1cc61e 100644
--- a/include/kitchensink/kitformats.h
+++ b/include/kitchensink/kitformats.h
@@ -4,7 +4,7 @@
#include <stdbool.h>
typedef struct Kit_AudioFormat {
- int stream_idx; ///< Stream index
+ int stream_index; ///< Stream index
bool is_enabled; ///< Is stream enabled
unsigned int format; ///< SDL Audio Format
bool is_signed; ///< Signedness
@@ -14,7 +14,7 @@ typedef struct Kit_AudioFormat {
} Kit_AudioFormat;
typedef struct Kit_VideoFormat {
- int stream_idx; ///< Stream index
+ int stream_index; ///< Stream index
bool is_enabled; ///< Is stream enabled
unsigned int format; ///< SDL Pixel Format
int width; ///< Width in pixels
@@ -22,7 +22,7 @@ typedef struct Kit_VideoFormat {
} Kit_VideoFormat;
typedef struct Kit_SubtitleFormat {
- int stream_idx; ///< Stream index
+ int stream_index; ///< Stream index
bool is_enabled; ///< Is stream enabled
} Kit_SubtitleFormat;
diff --git a/include/kitchensink/kitplayer.h b/include/kitchensink/kitplayer.h
index 9505208..a33828b 100644
--- a/include/kitchensink/kitplayer.h
+++ b/include/kitchensink/kitplayer.h
@@ -8,6 +8,7 @@
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_thread.h>
#include <SDL2/SDL_surface.h>
+#include <SDL2/SDL_mutex.h>
#include <stdbool.h>
@@ -26,47 +27,17 @@ typedef enum Kit_PlayerState {
} Kit_PlayerState;
typedef struct Kit_Player {
- // Local state
- Kit_PlayerState state; ///< Playback state
- Kit_VideoFormat vformat; ///< Video format information
- Kit_AudioFormat aformat; ///< Audio format information
+ Kit_PlayerState state; ///< Playback state
+ Kit_VideoFormat vformat; ///< Video format information
+ Kit_AudioFormat aformat; ///< Audio format information
Kit_SubtitleFormat sformat; ///< Subtitle format information
-
- // Synchronization
- double clock_sync; ///< Clock sync point
- double pause_start; ///< Timestamp of pause beginning
- double vclock_pos; ///< Video stream last pts
-
- // Threading
- SDL_Thread *dec_thread; ///< Decoder thread
- SDL_mutex *vmutex; ///< Video stream buffer lock
- SDL_mutex *amutex; ///< Audio stream buffer lock
- SDL_mutex *smutex; ///< Subtitle stream buffer lock
- SDL_mutex *cmutex; ///< Control stream buffer lock
-
- // Buffers
- void *abuffer; ///< Audio stream buffer
- void *vbuffer; ///< Video stream buffer
- void *sbuffer; ///< Subtitle stream buffer
- void *cbuffer; ///< Control stream buffer
-
- // FFmpeg internal state
- void *vcodec_ctx; ///< FFmpeg: Video codec context
- void *acodec_ctx; ///< FFmpeg: Audio codec context
- void *scodec_ctx; ///< FFmpeg: Subtitle codec context
- void *tmp_vframe; ///< FFmpeg: Preallocated temporary video frame
- void *tmp_aframe; ///< FFmpeg: Preallocated temporary audio frame
- void *tmp_sframe; ///< FFmpeg: Preallocated temporary subtitle frame
- void *swr; ///< FFmpeg: Audio resampler
- void *sws; ///< FFmpeg: Video converter
-
- // libass
- void *ass_renderer;
- void *ass_track;
-
- // Other
- uint8_t seek_flag;
- const Kit_Source *src; ///< Reference to Audio/Video source
+ void *audio_dec; ///< Audio decoder context (or NULL)
+ void *video_dec; ///< Video decoder context (or NULL)
+ void *subtitle_dec; ///< Subtitle decoder context (or NULL)
+ SDL_Thread *dec_thread; ///< Decoder thread
+ SDL_mutex *dec_lock; ///< Decoder lock
+ const Kit_Source *src; ///< Reference to Audio/Video source
+ double pause_started; ///< Temporary flag for handling pauses
} Kit_Player;
typedef struct Kit_PlayerInfo {
diff --git a/include/kitchensink/kitsource.h b/include/kitchensink/kitsource.h
index ed1711f..ea85d57 100644
--- a/include/kitchensink/kitsource.h
+++ b/include/kitchensink/kitsource.h
@@ -20,10 +20,10 @@ typedef enum Kit_StreamType {
} Kit_StreamType;
typedef struct Kit_Source {
- int astream_idx; ///< Audio stream index
- int vstream_idx; ///< Video stream index
- int sstream_idx; ///< Subtitle stream index
- void *format_ctx; ///< FFmpeg: Videostream format context
+ int audio_stream_index; ///< Audio stream index
+ int video_stream_index; ///< Video stream index
+ int subtitle_stream_index; ///< Subtitle stream index
+ void *format_ctx; ///< FFmpeg: Videostream format context
} Kit_Source;
typedef struct Kit_Stream {
diff --git a/src/kitaudio.c b/src/kitaudio.c
index 83385c8..e05948b 100644
--- a/src/kitaudio.c
+++ b/src/kitaudio.c
@@ -1,12 +1,34 @@
#include <assert.h>
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+#include <libavformat/avformat.h>
#include <libavutil/samplefmt.h>
#include <libswresample/swresample.h>
#include <SDL2/SDL.h>
+#include "kitchensink/kiterror.h"
#include "kitchensink/internal/kithelpers.h"
#include "kitchensink/internal/kitbuffer.h"
#include "kitchensink/internal/kitaudio.h"
+#include "kitchensink/internal/kitringbuffer.h"
+#include "kitchensink/internal/kitlog.h"
+
+#define KIT_AUDIO_IN_SIZE 32
+#define KIT_AUDIO_OUT_SIZE 16
+#define AUDIO_SYNC_THRESHOLD 0.05
+
+typedef struct Kit_AudioDecoder {
+ Kit_AudioFormat *format;
+ SwrContext *swr;
+ AVFrame *scratch_frame;
+} Kit_AudioDecoder;
+
+typedef struct Kit_AudioPacket {
+ double pts;
+ size_t original_size;
+ Kit_RingBuffer *rb;
+} Kit_AudioPacket;
Kit_AudioPacket* _CreateAudioPacket(const char* data, size_t len, double pts) {
@@ -33,7 +55,7 @@ enum AVSampleFormat _FindAVSampleFormat(int format) {
}
}
-unsigned int _FindAVChannelLayout(int channels) {
+int64_t _FindAVChannelLayout(int channels) {
switch(channels) {
case 1: return AV_CH_LAYOUT_MONO;
case 2: return AV_CH_LAYOUT_STEREO;
@@ -68,88 +90,217 @@ void _FindAudioFormat(enum AVSampleFormat fmt, int *bytes, bool *is_signed, unsi
}
}
-void _HandleAudioPacket(Kit_Player *player, AVPacket *packet) {
- assert(player != NULL);
- assert(packet != NULL);
+static void free_out_audio_packet_cb(void *packet) {
+ Kit_AudioPacket *p = packet;
+ Kit_DestroyRingBuffer(p->rb);
+ free(p);
+}
+static int dec_decode_audio_cb(Kit_Decoder *dec, AVPacket *in_packet) {
+ assert(dec != NULL);
+ assert(in_packet != NULL);
+
+ Kit_AudioDecoder *audio_dec = dec->userdata;
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);
+ // Decode as long as there is data
+ while(in_packet->size > 0) {
+ len = avcodec_decode_audio4(dec->codec_ctx, audio_dec->scratch_frame, &frame_finished, in_packet);
if(len < 0) {
- return;
+ return 1;
}
if(frame_finished) {
dst_nb_samples = av_rescale_rnd(
- aframe->nb_samples,
- player->aformat.samplerate,
- acodec_ctx->sample_rate,
+ audio_dec->scratch_frame->nb_samples,
+ audio_dec->format->samplerate, // Target samplerate
+ dec->codec_ctx->sample_rate, // Source samplerate
AV_ROUND_UP);
av_samples_alloc_array_and_samples(
&dst_data,
&dst_linesize,
- player->aformat.channels,
+ audio_dec->format->channels,
dst_nb_samples,
- _FindAVSampleFormat(player->aformat.format),
+ _FindAVSampleFormat(audio_dec->format->format),
0);
len2 = swr_convert(
- swr,
+ audio_dec->swr,
dst_data,
- aframe->nb_samples,
- (const unsigned char **)aframe->extended_data,
- aframe->nb_samples);
+ audio_dec->scratch_frame->nb_samples,
+ (const unsigned char **)audio_dec->scratch_frame->extended_data,
+ audio_dec->scratch_frame->nb_samples);
dst_bufsize = av_samples_get_buffer_size(
&dst_linesize,
- player->aformat.channels,
+ audio_dec->format->channels,
len2,
- _FindAVSampleFormat(player->aformat.format), 1);
+ _FindAVSampleFormat(audio_dec->format->format), 1);
- // Get pts
+ // Get pts for the packet
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;
+ if(in_packet->pts != AV_NOPTS_VALUE) {
+ pts = av_frame_get_best_effort_timestamp(audio_dec->scratch_frame);
+ pts *= av_q2d(dec->format_ctx->streams[dec->stream_index]->time_base);
}
// 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);
- }
+ Kit_AudioPacket *out_packet = _CreateAudioPacket(
+ (char*)dst_data[0], (size_t)dst_bufsize, pts);
+ Kit_WriteDecoderOutput(dec, out_packet);
+ // Free temps
av_freep(&dst_data[0]);
av_freep(&dst_data);
}
- packet->size -= len;
- packet->data += len;
+ in_packet->size -= len;
+ in_packet->data += len;
+ }
+
+
+ return 1;
+}
+
+static void dec_close_audio_cb(Kit_Decoder *dec) {
+ if(dec == NULL) return;
+
+ Kit_AudioDecoder *audio_dec = dec->userdata;
+ if(audio_dec->scratch_frame != NULL) {
+ av_frame_free(&audio_dec->scratch_frame);
+ }
+ if(audio_dec->swr != NULL) {
+ swr_free(&audio_dec->swr);
+ }
+ free(audio_dec);
+}
+
+Kit_Decoder* Kit_CreateAudioDecoder(const Kit_Source *src, Kit_AudioFormat *format) {
+ assert(src != NULL);
+ assert(format != NULL);
+ if(src->audio_stream_index < 0) {
+ return NULL;
+ }
+
+ // First the generic decoder component ...
+ Kit_Decoder *dec = Kit_CreateDecoder(
+ src, src->audio_stream_index,
+ KIT_AUDIO_IN_SIZE, KIT_AUDIO_OUT_SIZE,
+ free_out_audio_packet_cb);
+ if(dec == NULL) {
+ goto exit_0;
+ }
+
+ // Find formats
+ format->samplerate = dec->codec_ctx->sample_rate;
+ format->channels = dec->codec_ctx->channels > 2 ? 2 : dec->codec_ctx->channels;
+ format->is_enabled = true;
+ format->stream_index = src->audio_stream_index;
+ _FindAudioFormat(dec->codec_ctx->sample_fmt, &format->bytes, &format->is_signed, &format->format);
+
+ // ... then allocate the audio decoder
+ Kit_AudioDecoder *audio_dec = calloc(1, sizeof(Kit_AudioDecoder));
+ if(audio_dec == NULL) {
+ goto exit_1;
}
+
+ // Create temporary audio frame
+ audio_dec->scratch_frame = av_frame_alloc();
+ if(audio_dec->scratch_frame == NULL) {
+ Kit_SetError("Unable to initialize temporary audio frame");
+ goto exit_2;
+ }
+
+ // Create resampler
+ audio_dec->swr = swr_alloc_set_opts(
+ NULL,
+ _FindAVChannelLayout(format->channels), // Target channel layout
+ _FindAVSampleFormat(format->format), // Target fmt
+ format->samplerate, // Target samplerate
+ dec->codec_ctx->channel_layout, // Source channel layout
+ dec->codec_ctx->sample_fmt, // Source fmt
+ dec->codec_ctx->sample_rate, // Source samplerate
+ 0, NULL);
+
+ if(swr_init(audio_dec->swr) != 0) {
+ Kit_SetError("Unable to initialize audio resampler context");
+ goto exit_3;
+ }
+
+ // Set callbacks and userdata, and we're go
+ audio_dec->format = format;
+ dec->dec_decode = dec_decode_audio_cb;
+ dec->dec_close = dec_close_audio_cb;
+ dec->userdata = audio_dec;
+ return dec;
+
+exit_3:
+ av_frame_free(&audio_dec->scratch_frame);
+exit_2:
+ free(audio_dec);
+exit_1:
+ Kit_CloseDecoder(dec);
+exit_0:
+ return NULL;
+}
+
+int Kit_GetAudioDecoderData(Kit_Decoder *dec, unsigned char *buf, int len) {
+ assert(dec != NULL);
+
+ Kit_AudioPacket *packet = Kit_PeekDecoderOutput(dec);
+ if(packet == NULL) {
+ return 0;
+ }
+
+ int ret = 0;
+ Kit_AudioDecoder *audio_dec = dec->userdata;
+ int bytes_per_sample = audio_dec->format->bytes * audio_dec->format->channels;
+ double bytes_per_second = bytes_per_sample * audio_dec->format->samplerate;
+ double sync_ts = _GetSystemTime() - dec->clock_sync;
+
+ if(packet->pts > sync_ts + AUDIO_SYNC_THRESHOLD) {
+ return 0;
+ } else if(packet->pts < sync_ts - AUDIO_SYNC_THRESHOLD) {
+ // Audio is lagging, skip until good pts is found
+ while(1) {
+ Kit_AdvanceDecoderOutput(dec);
+ free_out_audio_packet_cb(packet);
+ packet = Kit_PeekDecoderOutput(dec);
+ if(packet == NULL) {
+ break;
+ } else {
+ dec->clock_pos = packet->pts;
+ }
+ if(packet->pts > sync_ts - AUDIO_SYNC_THRESHOLD) {
+ break;
+ }
+ }
+ }
+
+ // If we have no viable packet, just skip
+ if(packet == NULL) {
+ return 0;
+ }
+
+ // Read data from packet ringbuffer
+ if(len > 0) {
+ ret = Kit_ReadRingBuffer(packet->rb, (char*)buf, len);
+ }
+
+ // If ringbuffer is cleared, kill packet and advance buffer.
+ // Otherwise forward the pts value for the current packet.
+ if(Kit_GetRingBufferLength(packet->rb) == 0) {
+ Kit_AdvanceDecoderOutput(dec);
+ free_out_audio_packet_cb(packet);
+ } else {
+ packet->pts += ((double)ret) / bytes_per_second;
+ }
+ dec->clock_pos = packet->pts;
+
+ return ret;
}
diff --git a/src/kitcontrol.c b/src/kitcontrol.c
deleted file mode 100644
index 0a9a8d8..0000000
--- a/src/kitcontrol.c
+++ /dev/null
@@ -1,82 +0,0 @@
-
-#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/kitdecoder.c b/src/kitdecoder.c
new file mode 100644
index 0000000..2018097
--- /dev/null
+++ b/src/kitdecoder.c
@@ -0,0 +1,220 @@
+#include <stdlib.h>
+#include <assert.h>
+
+#include <libavformat/avformat.h>
+
+#include "kitchensink/internal/kitdecoder.h"
+#include "kitchensink/kiterror.h"
+
+
+static void free_in_video_packet_cb(void *packet) {
+ av_packet_free((AVPacket**)&packet);
+}
+
+Kit_Decoder* Kit_CreateDecoder(const Kit_Source *src, int stream_index,
+ int in_b_size, int out_b_size,
+ dec_free_packet_cb free_out_cb) {
+ assert(src != NULL);
+ assert(in_b_size > 0);
+ assert(out_b_size > 0);
+
+ AVCodecContext *codec_ctx = NULL;
+ AVCodec *codec = NULL;
+ AVFormatContext *format_ctx = src->format_ctx;
+ int bsizes[2] = {in_b_size, out_b_size};
+ dec_free_packet_cb free_hooks[2] = {free_in_video_packet_cb, free_out_cb};
+
+ // Make sure index seems correct
+ if(stream_index >= (int)format_ctx->nb_streams || stream_index < 0) {
+ Kit_SetError("Invalid stream %d", stream_index);
+ goto exit_0;
+ }
+
+ // Allocate decoder and make sure allocation was a success
+ Kit_Decoder *dec = calloc(1, sizeof(Kit_Decoder));
+ if(dec == NULL) {
+ Kit_SetError("Unable to allocate kit decoder for stream %d", stream_index);
+ goto exit_0;
+ }
+
+ // Find audio decoder
+ codec = avcodec_find_decoder(format_ctx->streams[stream_index]->codec->codec_id);
+ if(!codec) {
+ Kit_SetError("No suitable decoder found for stream %d", stream_index);
+ goto exit_1;
+ }
+
+ // Allocate a context for the codec
+ codec_ctx = avcodec_alloc_context3(codec);
+ if(avcodec_copy_context(codec_ctx, format_ctx->streams[stream_index]->codec) != 0) {
+ Kit_SetError("Unable to copy audio codec context for stream %d", stream_index);
+ goto exit_1;
+ }
+
+ // Open the stream
+ if(avcodec_open2(codec_ctx, codec, NULL) < 0) {
+ Kit_SetError("Unable to open codec for stream %d", stream_index);
+ goto exit_2;
+ }
+
+ // Set index and codec
+ dec->stream_index = stream_index;
+ dec->codec_ctx = codec_ctx;
+ dec->format_ctx = format_ctx;
+
+ // Allocate input/output ringbuffers and locks
+ for(int i = 0; i < 2; i++) {
+ dec->buffer[i] = Kit_CreateBuffer(bsizes[i], free_hooks[i]);
+ if(dec->buffer[i] == NULL) {
+ Kit_SetError("Unable to allocate ringbuffer type %d for stream %d", i, stream_index);
+ goto exit_3;
+ }
+
+ dec->lock[i] = SDL_CreateMutex();
+ if(dec->lock[i] == NULL) {
+ Kit_SetError("Unable to allocate mutex type %d for stream %d", i, stream_index);
+ goto exit_3;
+ }
+ }
+
+ // That's that
+ return dec;
+
+exit_3:
+ for(int i = 0; i < 2; i++) {
+ SDL_DestroyMutex(dec->lock[i]);
+ Kit_DestroyBuffer(dec->buffer[i]);
+ }
+ avcodec_close(codec_ctx);
+exit_2:
+ avcodec_free_context(&codec_ctx);
+exit_1:
+ free(dec);
+exit_0:
+ return NULL;
+}
+
+void Kit_SetDecoderClockSync(Kit_Decoder *dec, double sync) {
+ if(dec == NULL) return;
+ dec->clock_sync = sync;
+}
+
+void Kit_ChangeDecoderClockSync(Kit_Decoder *dec, double sync) {
+ if(dec == NULL) return;
+ dec->clock_sync += sync;
+}
+
+int Kit_WriteDecoderInput(Kit_Decoder *dec, AVPacket *packet) {
+ assert(dec != NULL);
+ int ret = 1;
+ if(SDL_LockMutex(dec->lock[KIT_DEC_IN]) == 0) {
+ ret = Kit_WriteBuffer(dec->buffer[KIT_DEC_IN], packet);
+ SDL_UnlockMutex(dec->lock[KIT_DEC_IN]);
+ }
+ return ret;
+}
+
+bool Kit_CanWriteDecoderInput(Kit_Decoder *dec) {
+ assert(dec != NULL);
+ bool ret = false;
+ if(SDL_LockMutex(dec->lock[KIT_DEC_IN]) == 0) {
+ ret = !Kit_IsBufferFull(dec->buffer[KIT_DEC_IN]);
+ SDL_UnlockMutex(dec->lock[KIT_DEC_IN]);
+ }
+ return ret;
+}
+
+AVPacket* Kit_ReadDecoderInput(Kit_Decoder *dec) {
+ assert(dec != NULL);
+ AVPacket *ret = NULL;
+ if(SDL_LockMutex(dec->lock[KIT_DEC_IN]) == 0) {
+ ret = Kit_ReadBuffer(dec->buffer[KIT_DEC_IN]);
+ SDL_UnlockMutex(dec->lock[KIT_DEC_IN]);
+ }
+ return ret;
+}
+
+int Kit_WriteDecoderOutput(Kit_Decoder *dec, void *packet) {
+ assert(dec != NULL);
+ int ret = 1;
+ if(SDL_LockMutex(dec->lock[KIT_DEC_OUT]) == 0) {
+ ret = Kit_WriteBuffer(dec->buffer[KIT_DEC_OUT], packet);
+ SDL_UnlockMutex(dec->lock[KIT_DEC_OUT]);
+ }
+ return ret;
+}
+
+void* Kit_PeekDecoderOutput(Kit_Decoder *dec) {
+ assert(dec != NULL);
+ void *ret = NULL;
+ if(SDL_LockMutex(dec->lock[KIT_DEC_OUT]) == 0) {
+ ret = Kit_PeekBuffer(dec->buffer[KIT_DEC_OUT]);
+ SDL_UnlockMutex(dec->lock[KIT_DEC_OUT]);
+ }
+ return ret;
+}
+
+void Kit_AdvanceDecoderOutput(Kit_Decoder *dec) {
+ assert(dec != NULL);
+ if(SDL_LockMutex(dec->lock[KIT_DEC_OUT]) == 0) {
+ Kit_AdvanceBuffer(dec->buffer[KIT_DEC_OUT]);
+ SDL_UnlockMutex(dec->lock[KIT_DEC_OUT]);
+ }
+}
+
+void Kit_ClearDecoderBuffers(Kit_Decoder *dec) {
+ if(dec == NULL) return;
+ if(SDL_LockMutex(dec->lock[KIT_DEC_IN]) == 0) {
+ Kit_ClearBuffer(dec->buffer[KIT_DEC_IN]);
+ SDL_UnlockMutex(dec->lock[KIT_DEC_IN]);
+ }
+ if(SDL_LockMutex(dec->lock[KIT_DEC_OUT]) == 0) {
+ Kit_ClearBuffer(dec->buffer[KIT_DEC_OUT]);
+ SDL_UnlockMutex(dec->lock[KIT_DEC_OUT]);
+ }
+ avcodec_flush_buffers(dec->codec_ctx);
+}
+
+int Kit_RunDecoder(Kit_Decoder *dec) {
+ if(dec == NULL) return 0;
+
+ AVPacket *in_packet;
+ int is_output_full;
+ int ret;
+
+ // First, check if there is room in output buffer
+ if(SDL_LockMutex(dec->lock[KIT_DEC_OUT]) == 0) {
+ is_output_full = Kit_IsBufferFull(dec->buffer[KIT_DEC_OUT]);
+ SDL_UnlockMutex(dec->lock[KIT_DEC_OUT]);
+ }
+ if(is_output_full) {
+ return 0;
+ }
+
+ // Then, see if we have incoming data
+ in_packet = Kit_ReadDecoderInput(dec);
+ if(in_packet == NULL) {
+ return 0;
+ }
+
+ // Run decoder with incoming packet
+ ret = dec->dec_decode(dec, in_packet);
+
+ // Free raw packet before returning
+ av_packet_free(&in_packet);
+ return ret;
+}
+
+void Kit_CloseDecoder(Kit_Decoder *dec) {
+ if(dec == NULL) return;
+ if(dec->dec_close) {
+ dec->dec_close(dec);
+ }
+ for(int i = 0; i < 2; i++) {
+ SDL_DestroyMutex(dec->lock[i]);
+ Kit_DestroyBuffer(dec->buffer[i]);
+ }
+ avcodec_close(dec->codec_ctx);
+ avcodec_free_context(&dec->codec_ctx);
+ free(dec);
+}
diff --git a/src/kitlib.c b/src/kitlib.c
index e5f1277..9ce1e27 100644
--- a/src/kitlib.c
+++ b/src/kitlib.c
@@ -24,10 +24,10 @@ int Kit_Init(unsigned int flags) {
state->init_flags = flags;
// Init libass
- state->libass_handle = ass_library_init();
+ //state->libass_handle = ass_library_init();
// Make libass message spam go away
- ass_set_message_cb(state->libass_handle, _libass_msg_callback, NULL);
+ //ass_set_message_cb(state->libass_handle, _libass_msg_callback, NULL);
return 0;
}
@@ -40,7 +40,7 @@ void Kit_Quit() {
}
state->init_flags = 0;
- ass_library_done(state->libass_handle);
+ //ass_library_done(state->libass_handle);
}
void Kit_GetVersion(Kit_Version *version) {
diff --git a/src/kitplayer.c b/src/kitplayer.c
index 480e815..809a5e2 100644
--- a/src/kitplayer.c
+++ b/src/kitplayer.c
@@ -1,226 +1,64 @@
#include "kitchensink/kitplayer.h"
#include "kitchensink/kiterror.h"
-#include "kitchensink/internal/kitbuffer.h"
-#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/samplefmt.h>
+#include "kitchensink/internal/kitlog.h"
#include <SDL2/SDL.h>
-#include <ass/ass.h>
-
-#include <stdlib.h>
-#include <string.h>
#include <assert.h>
-#include <math.h>
-#define __STDC_FORMAT_MACROS
-#include <inttypes.h>
-
-// For compatibility
-#ifndef ASS_FONTPROVIDER_AUTODETECT
-#define ASS_FONTPROVIDER_AUTODETECT 1
-#endif
-
-// Threshold is in seconds
-#define VIDEO_SYNC_THRESHOLD 0.01
-#define AUDIO_SYNC_THRESHOLD 0.05
-
-static int _InitCodecs(Kit_Player *player, const Kit_Source *src) {
- assert(player != NULL);
- assert(src != NULL);
-
- AVCodecContext *acodec_ctx = NULL;
- AVCodecContext *vcodec_ctx = NULL;
- AVCodecContext *scodec_ctx = NULL;
- AVCodec *acodec = NULL;
- AVCodec *vcodec = NULL;
- AVCodec *scodec = NULL;
- AVFormatContext *format_ctx = (AVFormatContext *)src->format_ctx;
-
- // Make sure index seems correct
- if(src->astream_idx >= (int)format_ctx->nb_streams) {
- Kit_SetError("Invalid audio stream index: %d", src->astream_idx);
- goto exit_0;
- } else if(src->astream_idx >= 0) {
- // Find audio decoder
- acodec = avcodec_find_decoder(format_ctx->streams[src->astream_idx]->codec->codec_id);
- if(!acodec) {
- Kit_SetError("No suitable audio decoder found");
- goto exit_0;
- }
-
- // Copy the original audio codec context
- acodec_ctx = avcodec_alloc_context3(acodec);
- if(avcodec_copy_context(acodec_ctx, format_ctx->streams[src->astream_idx]->codec) != 0) {
- Kit_SetError("Unable to copy audio codec context");
- goto exit_0;
- }
-
- // Create an audio decoder context
- if(avcodec_open2(acodec_ctx, acodec, NULL) < 0) {
- Kit_SetError("Unable to allocate audio codec context");
- goto exit_1;
- }
- }
-
- // Make sure index seems correct
- if(src->vstream_idx >= (int)format_ctx->nb_streams) {
- Kit_SetError("Invalid video stream index: %d", src->vstream_idx);
- goto exit_2;
- } else if(src->vstream_idx >= 0) {
- // Find video decoder
- vcodec = avcodec_find_decoder(format_ctx->streams[src->vstream_idx]->codec->codec_id);
- if(!vcodec) {
- Kit_SetError("No suitable video decoder found");
- goto exit_2;
- }
-
- // Copy the original video codec context
- vcodec_ctx = avcodec_alloc_context3(vcodec);
- if(avcodec_copy_context(vcodec_ctx, format_ctx->streams[src->vstream_idx]->codec) != 0) {
- Kit_SetError("Unable to copy video codec context");
- goto exit_2;
- }
-
- // Create a video decoder context
- if(avcodec_open2(vcodec_ctx, vcodec, NULL) < 0) {
- Kit_SetError("Unable to allocate video codec context");
- goto exit_3;
- }
- }
-
- if(src->sstream_idx >= (int)format_ctx->nb_streams) {
- Kit_SetError("Invalid subtitle stream index: %d", src->sstream_idx);
- goto exit_2;
- } else if(src->sstream_idx >= 0) {
- // Find subtitle decoder
- scodec = avcodec_find_decoder(format_ctx->streams[src->sstream_idx]->codec->codec_id);
- if(!scodec) {
- Kit_SetError("No suitable subtitle decoder found");
- goto exit_4;
- }
-
- // Copy the original subtitle codec context
- scodec_ctx = avcodec_alloc_context3(scodec);
- if(avcodec_copy_context(scodec_ctx, format_ctx->streams[src->sstream_idx]->codec) != 0) {
- Kit_SetError("Unable to copy subtitle codec context");
- goto exit_4;
- }
-
- // Create a subtitle decoder context
- if(avcodec_open2(scodec_ctx, scodec, NULL) < 0) {
- Kit_SetError("Unable to allocate subtitle codec context");
- goto exit_5;
- }
- }
-
- player->acodec_ctx = acodec_ctx;
- player->vcodec_ctx = vcodec_ctx;
- player->scodec_ctx = scodec_ctx;
- player->src = src;
- return 0;
-
-exit_5:
- avcodec_free_context(&scodec_ctx);
-exit_4:
- avcodec_close(vcodec_ctx);
-exit_3:
- avcodec_free_context(&vcodec_ctx);
-exit_2:
- avcodec_close(acodec_ctx);
-exit_1:
- avcodec_free_context(&acodec_ctx);
-exit_0:
- return 1;
-}
-
-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) {
- _HandleVideoPacket(player, packet);
- }
- else if(player->acodec_ctx != NULL && packet->stream_index == player->src->astream_idx) {
- _HandleAudioPacket(player, packet);
- }
- else if(player->scodec_ctx != NULL && packet->stream_index == player->src->sstream_idx) {
- _HandleSubtitlePacket(player, packet);
- }
-}
-
-static void _HandleControlPacket(Kit_Player *player, Kit_ControlPacket *packet) {
- switch(packet->type) {
- case KIT_CONTROL_FLUSH:
- _HandleFlushCommand(player, packet);
- break;
- case KIT_CONTROL_SEEK:
- _HandleSeekCommand(player, packet);
- break;
- }
-}
// Return 0 if stream is good but nothing else to do for now
// Return -1 if there is still work to be done
// Return 1 if there was an error or stream end
-static int _UpdatePlayer(Kit_Player *player) {
+static int _DemuxStream(const Kit_Player *player) {
assert(player != NULL);
- AVFormatContext *format_ctx = (AVFormatContext*)player->src->format_ctx;
-
- // Handle control queue
- if(SDL_LockMutex(player->cmutex) == 0) {
- Kit_ControlPacket *cpacket;
- while((cpacket = (Kit_ControlPacket*)Kit_ReadBuffer(player->cbuffer)) != NULL) {
- _HandleControlPacket(player, cpacket);
- _FreeControlPacket(cpacket);
- }
- SDL_UnlockMutex(player->cmutex);
- }
+ Kit_Decoder *video_dec = player->video_dec;
+ Kit_Decoder *audio_dec = player->audio_dec;
+ //Kit_Decoder *subtitle_dec = player->subtitle_dec;
+ AVFormatContext *format_ctx = player->src->format_ctx;
// If either buffer is full, just stop here for now.
// Since we don't know what kind of data is going to come out of av_read_frame, we really
// want to make sure we are prepared for everything :)
- if(player->vcodec_ctx != NULL) {
- if(SDL_LockMutex(player->vmutex) == 0) {
- int ret = Kit_IsBufferFull(player->vbuffer);
- SDL_UnlockMutex(player->vmutex);
- if(ret == 1) {
- return 0;
- }
+ if(video_dec != NULL) {
+ if(!Kit_CanWriteDecoderInput(video_dec)) {
+ return 0;
}
}
- if(player->acodec_ctx != NULL) {
- if(SDL_LockMutex(player->amutex) == 0) {
- int ret = Kit_IsBufferFull(player->abuffer);
- SDL_UnlockMutex(player->amutex);
- if(ret == 1) {
- return 0;
- }
+ if(audio_dec != NULL) {
+ if(!Kit_CanWriteDecoderInput(audio_dec)) {
+ return 0;
}
}
// Attempt to read frame. Just return here if it fails.
- AVPacket packet;
- if(av_read_frame(format_ctx, &packet) < 0) {
+ AVPacket *packet = av_packet_alloc();
+ if(av_read_frame(format_ctx, packet) < 0) {
+ av_packet_free(&packet);
return 1;
}
- _HandlePacket(player, &packet);
- av_packet_unref(&packet);
+
+ // Check if this is a packet we need to handle and pass it on
+ if(video_dec != NULL && packet->stream_index == video_dec->stream_index) {
+ Kit_WriteDecoderInput(video_dec, packet);
+ }
+ else if(audio_dec != NULL && packet->stream_index == audio_dec->stream_index) {
+ Kit_WriteDecoderInput(audio_dec, packet);
+ }
+ else {
+ av_packet_free(&packet);
+ }
+
return -1;
}
static int _DecoderThread(void *ptr) {
- Kit_Player *player = (Kit_Player*)ptr;
+ Kit_Player *player = ptr;
bool is_running = true;
bool is_playing = true;
int ret;
@@ -244,12 +82,23 @@ static int _DecoderThread(void *ptr) {
}
// Get more data from demuxer, decode. Wait a bit if there's no more work for now.
- ret = _UpdatePlayer(player);
- if(ret == 1) {
- player->state = KIT_STOPPED;
- } else if(ret == 0) {
- SDL_Delay(1);
+ if(SDL_LockMutex(player->dec_lock) == 0) {
+ while((ret = _DemuxStream(player)) == -1);
+ if(ret == 1) {
+ player->state = KIT_STOPPED;
+ continue;
+ }
+
+ // Run decoders for a bit
+ while(Kit_RunDecoder(player->video_dec) == 1);
+ while(Kit_RunDecoder(player->audio_dec) == 1);
+
+ // Free decoder thread lock.
+ SDL_UnlockMutex(player->dec_lock);
}
+
+ // We decoded as much as we could, sleep a bit.
+ SDL_Delay(1);
}
// Just idle while waiting for work.
@@ -268,279 +117,59 @@ Kit_Player* Kit_CreatePlayer(const Kit_Source *src) {
return NULL;
}
- AVCodecContext *acodec_ctx = NULL;
- AVCodecContext *vcodec_ctx = NULL;
- AVCodecContext *scodec_ctx = NULL;
-
- // Initialize codecs
- if(_InitCodecs(player, src) != 0) {
+ // Initialize audio decoder
+ player->audio_dec = Kit_CreateAudioDecoder(src, &player->aformat);
+ if(player->audio_dec == NULL && src->audio_stream_index >= 0) {
goto error;
}
- // Init audio codec information if audio codec is initialized
- acodec_ctx = (AVCodecContext*)player->acodec_ctx;
- if(acodec_ctx != NULL) {
- player->aformat.samplerate = acodec_ctx->sample_rate;
- player->aformat.channels = acodec_ctx->channels > 2 ? 2 : acodec_ctx->channels;
- player->aformat.is_enabled = true;
- player->aformat.stream_idx = src->astream_idx;
- _FindAudioFormat(acodec_ctx->sample_fmt, &player->aformat.bytes, &player->aformat.is_signed, &player->aformat.format);
-
- player->swr = swr_alloc_set_opts(
- NULL,
- _FindAVChannelLayout(player->aformat.channels), // Target channel layout
- _FindAVSampleFormat(player->aformat.format), // Target fmt
- player->aformat.samplerate, // Target samplerate
- acodec_ctx->channel_layout, // Source channel layout
- acodec_ctx->sample_fmt, // Source fmt
- acodec_ctx->sample_rate, // Source samplerate
- 0, NULL);
- if(swr_init((struct SwrContext *)player->swr) != 0) {
- Kit_SetError("Unable to initialize audio converter context");
- goto error;
- }
-
- player->abuffer = Kit_CreateBuffer(KIT_ABUFFERSIZE, _FreeAudioPacket);
- if(player->abuffer == NULL) {
- Kit_SetError("Unable to initialize audio ringbuffer");
- goto error;
- }
-
- player->tmp_aframe = av_frame_alloc();
- if(player->tmp_aframe == NULL) {
- Kit_SetError("Unable to initialize temporary audio frame");
- goto error;
- }
- }
-
- // Initialize video codec information is initialized
- vcodec_ctx = (AVCodecContext*)player->vcodec_ctx;
- if(vcodec_ctx != NULL) {
- player->vformat.is_enabled = true;
- player->vformat.width = vcodec_ctx->width;
- player->vformat.height = vcodec_ctx->height;
- player->vformat.stream_idx = src->vstream_idx;
- _FindPixelFormat(vcodec_ctx->pix_fmt, &player->vformat.format);
-
- player->sws = sws_getContext(
- vcodec_ctx->width, // Source w
- vcodec_ctx->height, // Source h
- vcodec_ctx->pix_fmt, // Source fmt
- vcodec_ctx->width, // Target w
- vcodec_ctx->height, // Target h
- _FindAVPixelFormat(player->vformat.format), // Target fmt
- SWS_BICUBIC,
- NULL, NULL, NULL);
- if((struct SwsContext *)player->sws == NULL) {
- Kit_SetError("Unable to initialize video converter context");
- goto error;
- }
-
- player->vbuffer = Kit_CreateBuffer(KIT_VBUFFERSIZE, _FreeVideoPacket);
- if(player->vbuffer == NULL) {
- Kit_SetError("Unable to initialize video ringbuffer");
- goto error;
- }
-
- player->tmp_vframe = av_frame_alloc();
- if(player->tmp_vframe == NULL) {
- Kit_SetError("Unable to initialize temporary video frame");
- goto error;
- }
- }
-
- // Initialize subtitle codec
- scodec_ctx = (AVCodecContext*)player->scodec_ctx;
- if(scodec_ctx != NULL) {
- player->sformat.is_enabled = true;
- player->sformat.stream_idx = src->sstream_idx;
-
- // subtitle packet buffer
- player->sbuffer = Kit_CreateList(KIT_SBUFFERSIZE, _FreeSubtitlePacket);
- if(player->sbuffer == NULL) {
- Kit_SetError("Unable to initialize active subtitle list");
- goto error;
- }
-
- // Initialize libass renderer
- Kit_LibraryState *state = Kit_GetLibraryState();
- player->ass_renderer = ass_renderer_init(state->libass_handle);
- if(player->ass_renderer == NULL) {
- Kit_SetError("Unable to initialize libass renderer");
- goto error;
- }
-
- // Read fonts from attachment streams and give them to libass
- AVFormatContext *format_ctx = player->src->format_ctx;
- for (int j = 0; j < format_ctx->nb_streams; j++) {
- AVStream *st = format_ctx->streams[j];
- if(st->codec->codec_type == AVMEDIA_TYPE_ATTACHMENT && attachment_is_font(st)) {
- const AVDictionaryEntry *tag = av_dict_get(
- st->metadata,
- "filename",
- NULL,
- AV_DICT_MATCH_CASE);
- if(tag) {
- ass_add_font(
- state->libass_handle,
- tag->value,
- (char*)st->codec->extradata,
- st->codec->extradata_size);
- }
- }
- }
-
- // Init libass fonts and window frame size
- ass_set_fonts(player->ass_renderer, NULL, "sans-serif", ASS_FONTPROVIDER_AUTODETECT, NULL, 1);
- ass_set_frame_size(player->ass_renderer, vcodec_ctx->width, vcodec_ctx->height);
- ass_set_hinting(player->ass_renderer, ASS_HINTING_NONE);
-
- // Initialize libass track
- player->ass_track = ass_new_track(state->libass_handle);
- if(player->ass_track == NULL) {
- Kit_SetError("Unable to initialize libass track");
- goto error;
- }
-
- // Set up libass track headers (ffmpeg provides these)
- if(scodec_ctx->subtitle_header) {
- ass_process_codec_private(
- (ASS_Track*)player->ass_track,
- (char*)scodec_ctx->subtitle_header,
- scodec_ctx->subtitle_header_size);
- }
- }
-
- player->cbuffer = Kit_CreateBuffer(KIT_CBUFFERSIZE, _FreeControlPacket);
- if(player->cbuffer == NULL) {
- Kit_SetError("Unable to initialize control ringbuffer");
+ // Initialize video decoder
+ player->video_dec = Kit_CreateVideoDecoder(src, &player->vformat);
+ if(player->video_dec == NULL && src->video_stream_index >= 0) {
goto error;
}
- player->vmutex = SDL_CreateMutex();
- if(player->vmutex == NULL) {
- Kit_SetError("Unable to allocate video mutex");
+ // Initialize subtitle decoder
+ player->subtitle_dec = Kit_CreateSubtitleDecoder(
+ src, &player->sformat, player->vformat.width, player->vformat.height);
+ if(player->subtitle_dec == NULL && src->subtitle_stream_index >= 0) {
goto error;
}
- player->amutex = SDL_CreateMutex();
- if(player->amutex == NULL) {
- Kit_SetError("Unable to allocate audio mutex");
- goto error;
- }
-
- player->cmutex = SDL_CreateMutex();
- if(player->cmutex == NULL) {
- Kit_SetError("Unable to allocate control buffer mutex");
- goto error;
- }
-
- player->smutex = SDL_CreateMutex();
- if(player->smutex == NULL) {
- Kit_SetError("Unable to allocate subtitle buffer mutex");
+ // Decoder thread lock
+ player->dec_lock = SDL_CreateMutex();
+ if(player->dec_lock == NULL) {
+ Kit_SetError("Unable to create a decoder thread: %s", SDL_GetError());
goto error;
}
+ // Decoder thread
player->dec_thread = SDL_CreateThread(_DecoderThread, "Kit Decoder Thread", player);
if(player->dec_thread == NULL) {
- Kit_SetError("Unable to create a decoder thread: %s", SDL_GetError());
+ Kit_SetError("Unable to create a decoder thread lock mutex: %s", SDL_GetError());
goto error;
}
+ player->src = src;
return player;
error:
- if(player->amutex != NULL) {
- SDL_DestroyMutex(player->amutex);
- }
- if(player->vmutex != NULL) {
- SDL_DestroyMutex(player->vmutex);
- }
- if(player->cmutex != NULL) {
- SDL_DestroyMutex(player->cmutex);
- }
- if(player->smutex != NULL) {
- SDL_DestroyMutex(player->smutex);
- }
- if(player->tmp_aframe != NULL) {
- av_frame_free((AVFrame**)&player->tmp_aframe);
- }
- if(player->tmp_vframe != NULL) {
- av_frame_free((AVFrame**)&player->tmp_vframe);
- }
-
- Kit_DestroyBuffer((Kit_Buffer*)player->vbuffer);
- Kit_DestroyBuffer((Kit_Buffer*)player->abuffer);
- Kit_DestroyBuffer((Kit_Buffer*)player->cbuffer);
- Kit_DestroyList((Kit_List*)player->sbuffer);
-
- if(player->sws != NULL) {
- sws_freeContext((struct SwsContext *)player->sws);
- }
- if(player->swr != NULL) {
- swr_free((struct SwrContext **)player->swr);
- }
-
- if(player->ass_track != NULL) {
- ass_free_track((ASS_Track*)player->ass_track);
- }
- if(player->ass_renderer != NULL) {
- ass_renderer_done((ASS_Renderer *)player->ass_renderer);
- }
- if(player != NULL) {
- free(player);
- }
+ Kit_ClosePlayer(player);
return NULL;
}
void Kit_ClosePlayer(Kit_Player *player) {
if(player == NULL) return;
- // Kill the decoder thread
+ // Kill the decoder thread and lock mutex
player->state = KIT_CLOSED;
SDL_WaitThread(player->dec_thread, NULL);
- SDL_DestroyMutex(player->vmutex);
- SDL_DestroyMutex(player->amutex);
- SDL_DestroyMutex(player->cmutex);
- SDL_DestroyMutex(player->smutex);
-
- // Free up converters
- if(player->sws != NULL) {
- sws_freeContext((struct SwsContext *)player->sws);
- }
- if(player->swr != NULL) {
- swr_free((struct SwrContext **)&player->swr);
- }
-
- // Free temporary frames
- if(player->tmp_vframe != NULL) {
- av_frame_free((AVFrame**)&player->tmp_vframe);
- }
- if(player->tmp_aframe != NULL) {
- av_frame_free((AVFrame**)&player->tmp_aframe);
- }
+ SDL_DestroyMutex(player->dec_lock);
- // Free contexts
- avcodec_close((AVCodecContext*)player->acodec_ctx);
- avcodec_close((AVCodecContext*)player->vcodec_ctx);
- avcodec_close((AVCodecContext*)player->scodec_ctx);
- avcodec_free_context((AVCodecContext**)&player->acodec_ctx);
- avcodec_free_context((AVCodecContext**)&player->vcodec_ctx);
- avcodec_free_context((AVCodecContext**)&player->scodec_ctx);
-
- // Free local audio buffers
- Kit_DestroyBuffer((Kit_Buffer*)player->cbuffer);
- Kit_DestroyBuffer((Kit_Buffer*)player->abuffer);
- Kit_DestroyBuffer((Kit_Buffer*)player->vbuffer);
- Kit_DestroyList((Kit_List*)player->sbuffer);
-
- // Free libass context
- if(player->ass_track != NULL) {
- ass_free_track((ASS_Track*)player->ass_track);
- }
- if(player->ass_renderer != NULL) {
- ass_renderer_done((ASS_Renderer *)player->ass_renderer);
- }
+ // Shutdown decoders
+ Kit_CloseDecoder(player->audio_dec);
+ Kit_CloseDecoder(player->video_dec);
+ Kit_CloseDecoder(player->subtitle_dec);
// Free the player structure itself
free(player);
@@ -548,13 +177,10 @@ void Kit_ClosePlayer(Kit_Player *player) {
int Kit_GetVideoData(Kit_Player *player, SDL_Texture *texture) {
assert(player != NULL);
-
- if(player->src->vstream_idx == -1) {
+ if(player->video_dec == NULL) {
return 0;
}
- assert(texture != NULL);
-
// If paused or stopped, do nothing
if(player->state == KIT_PAUSED) {
return 0;
@@ -563,80 +189,20 @@ int Kit_GetVideoData(Kit_Player *player, SDL_Texture *texture) {
return 0;
}
- // Read a packet from buffer, if one exists. Stop here if not.
- Kit_VideoPacket *packet = NULL;
- Kit_VideoPacket *n_packet = NULL;
- if(SDL_LockMutex(player->vmutex) == 0) {
- packet = (Kit_VideoPacket*)Kit_PeekBuffer((Kit_Buffer*)player->vbuffer);
- if(packet == NULL) {
- SDL_UnlockMutex(player->vmutex);
- return 0;
- }
-
- // Print some data
- double cur_video_ts = _GetSystemTime() - player->clock_sync;
-
- // Check if we want the packet
- if(packet->pts > cur_video_ts + VIDEO_SYNC_THRESHOLD) {
- // Video is ahead, don't show yet.
- SDL_UnlockMutex(player->vmutex);
- return 0;
- } else if(packet->pts < cur_video_ts - VIDEO_SYNC_THRESHOLD) {
- // Video is lagging, skip until we find a good PTS to continue from.
- while(packet != NULL) {
- Kit_AdvanceBuffer((Kit_Buffer*)player->vbuffer);
- n_packet = (Kit_VideoPacket*)Kit_PeekBuffer((Kit_Buffer*)player->vbuffer);
- if(n_packet == NULL) {
- break;
- }
- _FreeVideoPacket(packet);
- packet = n_packet;
- if(packet->pts > cur_video_ts - VIDEO_SYNC_THRESHOLD) {
- break;
- }
- }
- }
-
- // Advance buffer one frame forwards
- Kit_AdvanceBuffer((Kit_Buffer*)player->vbuffer);
- player->vclock_pos = packet->pts;
-
- // Update textures as required. Handle UYV frames separately.
- if(player->vformat.format == SDL_PIXELFORMAT_YV12
- || player->vformat.format == SDL_PIXELFORMAT_IYUV)
- {
- SDL_UpdateYUVTexture(
- texture, NULL,
- packet->frame->data[0], packet->frame->linesize[0],
- packet->frame->data[1], packet->frame->linesize[1],
- packet->frame->data[2], packet->frame->linesize[2]);
- }
- else {
- SDL_UpdateTexture(
- texture, NULL,
- packet->frame->data[0],
- packet->frame->linesize[0]);
- }
-
- _FreeVideoPacket(packet);
- SDL_UnlockMutex(player->vmutex);
- } else {
- Kit_SetError("Unable to lock video buffer mutex");
- return 1;
- }
-
- return 0;
+ return Kit_GetVideoDecoderData(player->video_dec, texture);
}
-int Kit_GetSubtitleData(Kit_Player *player, SDL_Renderer *renderer) {
+int Kit_GetAudioData(Kit_Player *player, unsigned char *buffer, int length, int cur_buf_len) {
assert(player != NULL);
-
- // If there is no audio stream, don't bother.
- if(player->src->sstream_idx == -1) {
+ assert(buffer != NULL);
+ if(player->audio_dec == NULL) {
return 0;
}
- assert(renderer != NULL);
+ // If asked for nothing, don't return anything either :P
+ if(length == 0) {
+ return 0;
+ }
// If paused or stopped, do nothing
if(player->state == KIT_PAUSED) {
@@ -646,57 +212,15 @@ int Kit_GetSubtitleData(Kit_Player *player, SDL_Renderer *renderer) {
return 0;
}
- unsigned int it;
- Kit_SubtitlePacket *packet = NULL;
-
- // Current sync timestamp
- double cur_subtitle_ts = _GetSystemTime() - player->clock_sync;
-
- // Read a packet from buffer, if one exists. Stop here if not.
- if(SDL_LockMutex(player->smutex) == 0) {
- // Check if refresh is required and remove old subtitles
- it = 0;
- while((packet = Kit_IterateList((Kit_List*)player->sbuffer, &it)) != NULL) {
- if(packet->pts_end >= 0 && packet->pts_end < cur_subtitle_ts) {
- Kit_RemoveFromList((Kit_List*)player->sbuffer, it);
- }
- }
-
- // Render subtitle bitmaps
- it = 0;
- while((packet = Kit_IterateList((Kit_List*)player->sbuffer, &it)) != NULL) {
- if(packet->texture == NULL) {
- packet->texture = SDL_CreateTextureFromSurface(renderer, packet->surface);
- SDL_SetTextureBlendMode(packet->texture, SDL_BLENDMODE_BLEND);
- }
- SDL_RenderCopy(renderer, packet->texture, NULL, packet->rect);
- }
-
- // Unlock subtitle buffer mutex.
- SDL_UnlockMutex(player->smutex);
- } else {
- Kit_SetError("Unable to lock subtitle buffer mutex");
- return 0;
- }
-
- return 0;
+ return Kit_GetAudioDecoderData(player->audio_dec, buffer, length);
}
-int Kit_GetAudioData(Kit_Player *player, unsigned char *buffer, int length, int cur_buf_len) {
+int Kit_GetSubtitleData(Kit_Player *player, SDL_Renderer *renderer) {
assert(player != NULL);
-
- // If there is no audio stream, don't bother.
- if(player->src->astream_idx == -1) {
+ if(player->subtitle_dec == NULL) {
return 0;
}
- // If asked for nothing, don't return anything either :P
- if(length == 0) {
- return 0;
- }
-
- assert(buffer != NULL);
-
// If paused or stopped, do nothing
if(player->state == KIT_PAUSED) {
return 0;
@@ -705,85 +229,21 @@ int Kit_GetAudioData(Kit_Player *player, unsigned char *buffer, int length, int
return 0;
}
- // Read a packet from buffer, if one exists. Stop here if not.
- int ret = 0;
- Kit_AudioPacket *packet = NULL;
- Kit_AudioPacket *n_packet = NULL;
- if(SDL_LockMutex(player->amutex) == 0) {
- packet = (Kit_AudioPacket*)Kit_PeekBuffer((Kit_Buffer*)player->abuffer);
- if(packet == NULL) {
- SDL_UnlockMutex(player->amutex);
- return 0;
- }
-
- int bytes_per_sample = player->aformat.bytes * player->aformat.channels;
- double bps = bytes_per_sample * player->aformat.samplerate;
- double cur_audio_ts = _GetSystemTime() - player->clock_sync + ((double)cur_buf_len / bps);
- double diff = cur_audio_ts - packet->pts;
- int diff_samples = fabs(diff) * player->aformat.samplerate;
-
- if(packet->pts > cur_audio_ts + AUDIO_SYNC_THRESHOLD) {
- // Audio is ahead, fill buffer with some silence
- int max_diff_samples = length / bytes_per_sample;
- int max_samples = (max_diff_samples < diff_samples) ? max_diff_samples : diff_samples;
-
- av_samples_set_silence(
- &buffer,
- 0, // Offset
- max_samples,
- player->aformat.channels,
- _FindAVSampleFormat(player->aformat.format));
-
- int diff_bytes = max_samples * bytes_per_sample;
-
- SDL_UnlockMutex(player->amutex);
- return diff_bytes;
-
- } else if(packet->pts < cur_audio_ts - AUDIO_SYNC_THRESHOLD) {
- // Audio is lagging, skip until good pts is found
-
- while(1) {
- Kit_AdvanceBuffer((Kit_Buffer*)player->abuffer);
- n_packet = (Kit_AudioPacket*)Kit_PeekBuffer((Kit_Buffer*)player->abuffer);
- if(n_packet != NULL) {
- packet = n_packet;
- } else {
- break;
- }
- if(packet->pts > cur_audio_ts - AUDIO_SYNC_THRESHOLD) {
- break;
- }
- }
- }
-
- if(length > 0) {
- ret = Kit_ReadRingBuffer(packet->rb, (char*)buffer, length);
- }
- if(Kit_GetRingBufferLength(packet->rb) == 0) {
- Kit_AdvanceBuffer((Kit_Buffer*)player->abuffer);
- _FreeAudioPacket(packet);
- } else {
- double adjust = (double)ret / bps;
- packet->pts += adjust;
- }
-
- SDL_UnlockMutex(player->amutex);
- } else {
- Kit_SetError("Unable to lock audio buffer mutex");
- return 0;
- }
- return ret;
+ return 0;
}
void Kit_GetPlayerInfo(const Kit_Player *player, Kit_PlayerInfo *info) {
assert(player != NULL);
assert(info != NULL);
- AVCodecContext *acodec_ctx = (AVCodecContext*)player->acodec_ctx;
- AVCodecContext *vcodec_ctx = (AVCodecContext*)player->vcodec_ctx;
- AVCodecContext *scodec_ctx = (AVCodecContext*)player->scodec_ctx;
+ Kit_Decoder *audio_dec = player->audio_dec;
+ Kit_Decoder *video_dec = player->video_dec;
+ Kit_Decoder *subtitle_dec = player->subtitle_dec;
+ AVCodecContext *acodec_ctx = audio_dec ? audio_dec->codec_ctx : NULL;
+ AVCodecContext *vcodec_ctx = video_dec ? video_dec->codec_ctx : NULL;
+ AVCodecContext *scodec_ctx = subtitle_dec ? subtitle_dec->codec_ctx : NULL;
// Reset everything to 0. We might not fill all fields.
memset(info, 0, sizeof(Kit_PlayerInfo));
@@ -805,58 +265,99 @@ void Kit_GetPlayerInfo(const Kit_Player *player, Kit_PlayerInfo *info) {
}
}
+static void _SetClockSync(Kit_Player *player) {
+ if(SDL_LockMutex(player->dec_lock) == 0) {
+ double sync = _GetSystemTime();
+ Kit_SetDecoderClockSync(player->video_dec, sync);
+ Kit_SetDecoderClockSync(player->audio_dec, sync);
+ Kit_SetDecoderClockSync(player->subtitle_dec, sync);
+ SDL_UnlockMutex(player->dec_lock);
+ }
+}
+
+static void _ChangeClockSync(Kit_Player *player, double delta) {
+ if(SDL_LockMutex(player->dec_lock) == 0) {
+ Kit_ChangeDecoderClockSync(player->video_dec, delta);
+ Kit_ChangeDecoderClockSync(player->audio_dec, delta);
+ Kit_ChangeDecoderClockSync(player->subtitle_dec, delta);
+ SDL_UnlockMutex(player->dec_lock);
+ }
+}
+
Kit_PlayerState Kit_GetPlayerState(const Kit_Player *player) {
assert(player != NULL);
-
return player->state;
}
void Kit_PlayerPlay(Kit_Player *player) {
assert(player != NULL);
-
- if(player->state == KIT_PLAYING) {
- return;
- }
- if(player->state == KIT_STOPPED) {
- player->clock_sync = _GetSystemTime();
- }
- if(player->state == KIT_PAUSED) {
- player->clock_sync += _GetSystemTime() - player->pause_start;
+ double tmp;
+ switch(player->state) {
+ case KIT_PLAYING:
+ case KIT_CLOSED:
+ break;
+ case KIT_PAUSED:
+ tmp = _GetSystemTime() - player->pause_started;
+ _ChangeClockSync(player, tmp);
+ player->state = KIT_PLAYING;
+ break;
+ case KIT_STOPPED:
+ _SetClockSync(player);
+ player->state = KIT_PLAYING;
+ break;
}
- player->state = KIT_PLAYING;
}
void Kit_PlayerStop(Kit_Player *player) {
assert(player != NULL);
-
- if(player->state == KIT_STOPPED) {
- return;
+ switch(player->state) {
+ case KIT_STOPPED:
+ case KIT_CLOSED:
+ break;
+ case KIT_PLAYING:
+ case KIT_PAUSED:
+ player->state = KIT_STOPPED;
+ break;
}
- player->state = KIT_STOPPED;
}
void Kit_PlayerPause(Kit_Player *player) {
assert(player != NULL);
-
- if(player->state != KIT_PLAYING) {
- return;
- }
- player->pause_start = _GetSystemTime();
player->state = KIT_PAUSED;
+ player->pause_started = _GetSystemTime();
}
-int Kit_PlayerSeek(Kit_Player *player, double m_time) {
+int Kit_PlayerSeek(Kit_Player *player, double seek_set) {
assert(player != NULL);
+ double position, duration;
+ int64_t seek_target;
- // Send packets to control stream
- if(SDL_LockMutex(player->cmutex) == 0) {
- // Flush audio and video buffers, then set seek, then unlock control queue mutex.
- Kit_WriteBuffer((Kit_Buffer*)player->cbuffer, _CreateControlPacket(KIT_CONTROL_FLUSH, 0));
- Kit_WriteBuffer((Kit_Buffer*)player->cbuffer, _CreateControlPacket(KIT_CONTROL_SEEK, m_time));
- SDL_UnlockMutex(player->cmutex);
- } else {
- Kit_SetError("Unable to lock control queue mutex");
- return 1;
+ if(SDL_LockMutex(player->dec_lock) == 0) {
+ duration = Kit_GetPlayerDuration(player);
+ position = Kit_GetPlayerPosition(player);
+ if(seek_set <= 0) {
+ seek_set = 0;
+ }
+ if(seek_set >= duration) {
+ seek_set = duration;
+ }
+
+ // Set source to timestamp
+ AVFormatContext *format_ctx = player->src->format_ctx;
+ seek_target = seek_set * AV_TIME_BASE;
+ if(avformat_seek_file(format_ctx, -1, INT64_MIN, seek_target, seek_target, 0) < 0) {
+ Kit_SetError("Unable to seek source");
+ return 1;
+ } else {
+ _ChangeClockSync(player, position - seek_set);
+ Kit_ClearDecoderBuffers(player->video_dec);
+ Kit_ClearDecoderBuffers(player->audio_dec);
+ Kit_ClearDecoderBuffers(player->subtitle_dec);
+ while(_DemuxStream(player) == -1);
+ }
+
+ // That's it. Unlock and continue.
+ SDL_UnlockMutex(player->dec_lock);
}
return 0;
@@ -865,12 +366,18 @@ int Kit_PlayerSeek(Kit_Player *player, double m_time) {
double Kit_GetPlayerDuration(const Kit_Player *player) {
assert(player != NULL);
- AVFormatContext *fmt_ctx = (AVFormatContext *)player->src->format_ctx;
+ AVFormatContext *fmt_ctx = player->src->format_ctx;
return (fmt_ctx->duration / AV_TIME_BASE);
}
double Kit_GetPlayerPosition(const Kit_Player *player) {
assert(player != NULL);
- return player->vclock_pos;
+ if(player->video_dec) {
+ return ((Kit_Decoder*)player->video_dec)->clock_pos;
+ }
+ if(player->audio_dec) {
+ return ((Kit_Decoder*)player->audio_dec)->clock_pos;
+ }
+ return 0;
}
diff --git a/src/kitsource.c b/src/kitsource.c
index 075bcbd..c9c8f4c 100644
--- a/src/kitsource.c
+++ b/src/kitsource.c
@@ -34,9 +34,9 @@ Kit_Source* Kit_CreateSourceFromUrl(const char *url) {
}
// Find best streams for defaults
- src->astream_idx = Kit_GetBestSourceStream(src, KIT_STREAMTYPE_AUDIO);
- src->vstream_idx = Kit_GetBestSourceStream(src, KIT_STREAMTYPE_VIDEO);
- src->sstream_idx = Kit_GetBestSourceStream(src, KIT_STREAMTYPE_SUBTITLE);
+ src->audio_stream_index = Kit_GetBestSourceStream(src, KIT_STREAMTYPE_AUDIO);
+ src->video_stream_index = Kit_GetBestSourceStream(src, KIT_STREAMTYPE_VIDEO);
+ src->subtitle_stream_index = Kit_GetBestSourceStream(src, KIT_STREAMTYPE_SUBTITLE);
return src;
exit_1:
@@ -102,9 +102,9 @@ int Kit_GetBestSourceStream(const Kit_Source *src, const Kit_StreamType type) {
int Kit_SetSourceStream(Kit_Source *src, const Kit_StreamType type, int index) {
assert(src != NULL);
switch(type) {
- case KIT_STREAMTYPE_AUDIO: src->astream_idx = index; break;
- case KIT_STREAMTYPE_VIDEO: src->vstream_idx = index; break;
- case KIT_STREAMTYPE_SUBTITLE: src->sstream_idx = index; break;
+ case KIT_STREAMTYPE_AUDIO: src->audio_stream_index = index; break;
+ case KIT_STREAMTYPE_VIDEO: src->video_stream_index = index; break;
+ case KIT_STREAMTYPE_SUBTITLE: src->subtitle_stream_index = index; break;
default:
Kit_SetError("Invalid stream type");
return 1;
@@ -115,9 +115,9 @@ int Kit_SetSourceStream(Kit_Source *src, const Kit_StreamType type, int index) {
int Kit_GetSourceStream(const Kit_Source *src, const Kit_StreamType type) {
assert(src != NULL);
switch(type) {
- case KIT_STREAMTYPE_AUDIO: return src->astream_idx;
- case KIT_STREAMTYPE_VIDEO: return src->vstream_idx;
- case KIT_STREAMTYPE_SUBTITLE: return src->sstream_idx;
+ case KIT_STREAMTYPE_AUDIO: return src->audio_stream_index;
+ case KIT_STREAMTYPE_VIDEO: return src->video_stream_index;
+ case KIT_STREAMTYPE_SUBTITLE: return src->subtitle_stream_index;
default:
break;
}
diff --git a/src/kitsubtitle.c b/src/kitsubtitle.c
index 82a48c2..60fd144 100644
--- a/src/kitsubtitle.c
+++ b/src/kitsubtitle.c
@@ -2,12 +2,38 @@
#include <SDL2/SDL.h>
#include <ass/ass.h>
+#include <libavformat/avformat.h>
+#include "kitchensink/kiterror.h"
+#include "kitchensink/internal/kitlibstate.h"
#include "kitchensink/internal/kitsubtitle.h"
#include "kitchensink/internal/kitlist.h"
+#include "kitchensink/internal/kithelpers.h"
+// For compatibility
+#ifndef ASS_FONTPROVIDER_AUTODETECT
+#define ASS_FONTPROVIDER_AUTODETECT 1
+#endif
-Kit_SubtitlePacket* _CreateSubtitlePacket(double pts_start, double pts_end, SDL_Rect *rect, SDL_Surface *surface) {
+#define KIT_SUBTITLE_IN_SIZE 64
+#define KIT_SUBTITLE_OUT_SIZE 64
+
+typedef struct Kit_SubtitleDecoder {
+ Kit_SubtitleFormat *format;
+ ASS_Renderer *ass_renderer;
+ ASS_Track *ass_track;
+} Kit_SubtitleDecoder;
+
+typedef struct Kit_SubtitlePacket {
+ double pts_start;
+ double pts_end;
+ SDL_Rect *rect;
+ SDL_Surface *surface;
+ SDL_Texture *texture;
+} Kit_SubtitlePacket;
+
+/*
+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;
@@ -17,15 +43,6 @@ Kit_SubtitlePacket* _CreateSubtitlePacket(double pts_start, double pts_end, SDL_
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) {
@@ -163,7 +180,7 @@ void _HandleSubtitlePacket(Kit_Player *player, AVPacket *packet) {
if(frame_finished) {
// Get pts
double pts = 0;
- if(packet->dts != AV_NOPTS_VALUE) {
+ if(packet->pts != AV_NOPTS_VALUE) {
pts = packet->pts;
pts *= av_q2d(fmt_ctx->streams[player->src->sstream_idx]->time_base);
}
@@ -232,3 +249,198 @@ void _HandleSubtitlePacket(Kit_Player *player, AVPacket *packet) {
}
}
}
+*/
+
+static void free_out_subtitle_packet_cb(void *packet) {
+ Kit_SubtitlePacket *p = packet;
+ SDL_FreeSurface(p->surface);
+ if(p->texture) {
+ SDL_DestroyTexture(p->texture);
+ }
+ free(p->rect);
+ free(p);
+}
+
+static int dec_decode_subtitle_cb(Kit_Decoder *dec, AVPacket *in_packet) {
+ assert(dec != NULL);
+ assert(in_packet != NULL);
+
+ int frame_finished;
+ int len;
+
+ AVSubtitle sub;
+ memset(&sub, 0, sizeof(AVSubtitle));
+
+ if(in_packet->size > 0) {
+ len = avcodec_decode_subtitle2(dec->codec_ctx, &sub, &frame_finished, in_packet);
+ if(len < 0) {
+ return 1;
+ }
+
+ if(frame_finished) {
+ // Get pts
+ double pts = 0;
+ if(in_packet->dts != AV_NOPTS_VALUE) {
+ pts = in_packet->pts;
+ pts *= av_q2d(dec->format_ctx->streams[dec->stream_index]->time_base);
+ }
+
+
+ // TODO: Implement more stuff
+ }
+ }
+
+ return 1;
+}
+
+static void dec_close_subtitle_cb(Kit_Decoder *dec) {
+ if(dec == NULL) return;
+
+ Kit_SubtitleDecoder *subtitle_dec = dec->userdata;
+ /*
+ if(subtitle_dec->ass_track != NULL) {
+ ass_free_track(subtitle_dec->ass_track);
+ }
+ if(subtitle_dec->ass_renderer != NULL) {
+ ass_renderer_done(subtitle_dec->ass_renderer);
+ }*/
+ free(subtitle_dec);
+}
+
+Kit_Decoder* Kit_CreateSubtitleDecoder(const Kit_Source *src, Kit_SubtitleFormat *format, int w, int h) {
+ assert(src != NULL);
+ assert(format != NULL);
+ if(src->subtitle_stream_index < 0) {
+ return NULL;
+ }
+
+ // First the generic decoder component ...
+ Kit_Decoder *dec = Kit_CreateDecoder(
+ src, src->subtitle_stream_index,
+ KIT_SUBTITLE_IN_SIZE, KIT_SUBTITLE_OUT_SIZE,
+ free_out_subtitle_packet_cb);
+ if(dec == NULL) {
+ goto exit_0;
+ }
+
+ // Find formats
+ format->is_enabled = true;
+ format->stream_index = src->subtitle_stream_index;
+
+ // ... then allocate the subtitle decoder
+ Kit_SubtitleDecoder *subtitle_dec = calloc(1, sizeof(Kit_SubtitleDecoder));
+ if(subtitle_dec == NULL) {
+ goto exit_1;
+ }
+
+ /*
+ // Initialize libass renderer
+ Kit_LibraryState *state = Kit_GetLibraryState();
+ subtitle_dec->ass_renderer = ass_renderer_init(state->libass_handle);
+ if(subtitle_dec->ass_renderer == NULL) {
+ Kit_SetError("Unable to initialize libass renderer");
+ goto exit_2;
+ }
+
+ // Read fonts from attachment streams and give them to libass
+ AVFormatContext *format_ctx = src->format_ctx;
+ for (int j = 0; j < format_ctx->nb_streams; j++) {
+ AVStream *st = format_ctx->streams[j];
+ if(st->codec->codec_type == AVMEDIA_TYPE_ATTACHMENT && attachment_is_font(st)) {
+ const AVDictionaryEntry *tag = av_dict_get(
+ st->metadata,
+ "filename",
+ NULL,
+ AV_DICT_MATCH_CASE);
+ if(tag) {
+ ass_add_font(
+ state->libass_handle,
+ tag->value,
+ (char*)st->codec->extradata,
+ st->codec->extradata_size);
+ }
+ }
+ }
+
+ // Init libass fonts and window frame size
+ ass_set_fonts(
+ subtitle_dec->ass_renderer,
+ NULL, "sans-serif",
+ ASS_FONTPROVIDER_AUTODETECT,
+ NULL, 1);
+ ass_set_frame_size(subtitle_dec->ass_renderer, w, h);
+ ass_set_hinting(subtitle_dec->ass_renderer, ASS_HINTING_NONE);
+
+ // Initialize libass track
+ subtitle_dec->ass_track = ass_new_track(state->libass_handle);
+ if(subtitle_dec->ass_track == NULL) {
+ Kit_SetError("Unable to initialize libass track");
+ goto exit_3;
+ }
+
+ // Set up libass track headers (ffmpeg provides these)
+ if(dec->codec_ctx->subtitle_header) {
+ ass_process_codec_private(
+ subtitle_dec->ass_track,
+ (char*)dec->codec_ctx->subtitle_header,
+ dec->codec_ctx->subtitle_header_size);
+ }*/
+
+ // Set callbacks and userdata, and we're go
+ subtitle_dec->format = format;
+ dec->dec_decode = dec_decode_subtitle_cb;
+ dec->dec_close = dec_close_subtitle_cb;
+ dec->userdata = subtitle_dec;
+ return dec;
+
+//exit_3:
+ //ass_renderer_done(subtitle_dec->ass_renderer);
+//exit_2:
+ //free(subtitle_dec);
+exit_1:
+ Kit_CloseDecoder(dec);
+exit_0:
+ return NULL;
+}
+
+int Kit_GetSubtitleDecoderData(Kit_Decoder *dec, SDL_Renderer *renderer) {
+ assert(dec != NULL);
+ assert(renderer != NULL);
+
+
+/*
+ unsigned int it;
+ Kit_SubtitlePacket *packet = NULL;
+
+ // Current sync timestamp
+ double cur_subtitle_ts = _GetSystemTime() - player->clock_sync;
+
+ // Read a packet from buffer, if one exists. Stop here if not.
+ if(SDL_LockMutex(player->smutex) == 0) {
+ // Check if refresh is required and remove old subtitles
+ it = 0;
+ while((packet = Kit_IterateList((Kit_List*)player->sbuffer, &it)) != NULL) {
+ if(packet->pts_end >= 0 && packet->pts_end < cur_subtitle_ts) {
+ Kit_RemoveFromList((Kit_List*)player->sbuffer, it);
+ }
+ }
+
+ // Render subtitle bitmaps
+ it = 0;
+ while((packet = Kit_IterateList((Kit_List*)player->sbuffer, &it)) != NULL) {
+ if(packet->texture == NULL) {
+ packet->texture = SDL_CreateTextureFromSurface(renderer, packet->surface);
+ SDL_SetTextureBlendMode(packet->texture, SDL_BLENDMODE_BLEND);
+ }
+ SDL_RenderCopy(renderer, packet->texture, NULL, packet->rect);
+ }
+
+ // Unlock subtitle buffer mutex.
+ SDL_UnlockMutex(player->smutex);
+ } else {
+ Kit_SetError("Unable to lock subtitle buffer mutex");
+ return 0;
+ }
+*/
+ return 0;
+}
diff --git a/src/kitvideo.c b/src/kitvideo.c
index d68b835..4a83056 100644
--- a/src/kitvideo.c
+++ b/src/kitvideo.c
@@ -1,28 +1,40 @@
#include <assert.h>
+#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
-#include "kitchensink/internal/kitvideo.h"
+#include "kitchensink/kiterror.h"
+#include "kitchensink/internal/kitdecoder.h"
#include "kitchensink/internal/kithelpers.h"
#include "kitchensink/internal/kitbuffer.h"
+#include "kitchensink/internal/kitvideo.h"
+#include "kitchensink/internal/kitlog.h"
+
+#define KIT_VIDEO_IN_SIZE 32
+#define KIT_VIDEO_OUT_SIZE 1
+#define VIDEO_SYNC_THRESHOLD 0.01
+
+typedef struct Kit_VideoDecoder {
+ Kit_VideoFormat *format;
+ struct SwsContext *sws;
+ AVFrame *scratch_frame;
+} Kit_VideoDecoder;
+typedef struct Kit_VideoPacket {
+ double pts;
+ AVFrame *frame;
+} Kit_VideoPacket;
-Kit_VideoPacket* _CreateVideoPacket(AVFrame *frame, double pts) {
+
+static 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) {
+static void _FindPixelFormat(enum AVPixelFormat fmt, unsigned int *out_fmt) {
switch(fmt) {
case AV_PIX_FMT_YUV420P9:
case AV_PIX_FMT_YUV420P10:
@@ -44,7 +56,7 @@ void _FindPixelFormat(enum AVPixelFormat fmt, unsigned int *out_fmt) {
}
}
-enum AVPixelFormat _FindAVPixelFormat(unsigned int fmt) {
+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;
@@ -57,72 +69,208 @@ enum AVPixelFormat _FindAVPixelFormat(unsigned int fmt) {
}
}
-void _HandleVideoPacket(Kit_Player *player, AVPacket *packet) {
- assert(player != NULL);
- assert(packet != NULL);
+static void free_out_video_packet_cb(void *packet) {
+ Kit_VideoPacket *p = packet;
+ av_freep(&p->frame->data[0]);
+ av_frame_free(&p->frame);
+ free(p);
+}
+
+static int dec_decode_video_cb(Kit_Decoder *dec, AVPacket *in_packet) {
+ assert(dec != NULL);
+ assert(in_packet != NULL);
+ Kit_VideoDecoder *video_dec = dec->userdata;
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);
+ while(in_packet->size > 0) {
+ int len = avcodec_decode_video2(dec->codec_ctx, video_dec->scratch_frame, &frame_finished, in_packet);
if(len < 0) {
- return;
+ return 1;
}
if(frame_finished) {
// Target frame
- AVFrame *oframe = av_frame_alloc();
+ AVFrame *out_frame = av_frame_alloc();
av_image_alloc(
- oframe->data,
- oframe->linesize,
- vcodec_ctx->width,
- vcodec_ctx->height,
- _FindAVPixelFormat(player->vformat.format),
+ out_frame->data,
+ out_frame->linesize,
+ dec->codec_ctx->width,
+ dec->codec_ctx->height,
+ _FindAVPixelFormat(video_dec->format->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,
+ video_dec->sws,
+ (const unsigned char * const *)video_dec->scratch_frame->data,
+ video_dec->scratch_frame->linesize,
0,
- vcodec_ctx->height,
- oframe->data,
- oframe->linesize);
+ dec->codec_ctx->height,
+ out_frame->data,
+ out_frame->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;
+ if(in_packet->pts != AV_NOPTS_VALUE) {
+ pts = av_frame_get_best_effort_timestamp(video_dec->scratch_frame);
+ pts *= av_q2d(dec->format_ctx->streams[dec->stream_index]->time_base);
}
// 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);
- }
+ Kit_VideoPacket *out_packet = _CreateVideoPacket(out_frame, pts);
+ Kit_WriteDecoderOutput(dec, out_packet);
+ }
+ in_packet->size -= len;
+ in_packet->data += len;
+ }
+
+
+ return 1;
+}
+
+static void dec_close_video_cb(Kit_Decoder *dec) {
+ if(dec == NULL) return;
+
+ Kit_VideoDecoder *video_dec = dec->userdata;
+ if(video_dec->scratch_frame != NULL) {
+ av_frame_free(&video_dec->scratch_frame);
+ }
+ if(video_dec->sws != NULL) {
+ sws_freeContext(video_dec->sws);
+ }
+ free(video_dec);
+}
+
+Kit_Decoder* Kit_CreateVideoDecoder(const Kit_Source *src, Kit_VideoFormat *format) {
+ assert(src != NULL);
+ assert(format != NULL);
+ if(src->video_stream_index < 0) {
+ return NULL;
+ }
+
+ // First the generic decoder component ...
+ Kit_Decoder *dec = Kit_CreateDecoder(
+ src, src->video_stream_index,
+ KIT_VIDEO_IN_SIZE, KIT_VIDEO_OUT_SIZE,
+ free_out_video_packet_cb);
+ if(dec == NULL) {
+ goto exit_0;
+ }
- // Unable to write packet, free it.
- if(!done) {
- _FreeVideoPacket(vpacket);
+ // Find formats
+ format->is_enabled = true;
+ format->width = dec->codec_ctx->width;
+ format->height = dec->codec_ctx->height;
+ format->stream_index = src->video_stream_index;
+ _FindPixelFormat(dec->codec_ctx->pix_fmt, &format->format);
+
+ // ... then allocate the video decoder
+ Kit_VideoDecoder *video_dec = calloc(1, sizeof(Kit_VideoDecoder));
+ if(video_dec == NULL) {
+ goto exit_1;
+ }
+
+ // Create temporary video frame
+ video_dec->scratch_frame = av_frame_alloc();
+ if(video_dec->scratch_frame == NULL) {
+ Kit_SetError("Unable to initialize temporary video frame");
+ goto exit_2;
+ }
+
+ // Create scaler
+ video_dec->sws = sws_getContext(
+ dec->codec_ctx->width, // Source w
+ dec->codec_ctx->height, // Source h
+ dec->codec_ctx->pix_fmt, // Source fmt
+ dec->codec_ctx->width, // Target w
+ dec->codec_ctx->height, // Target h
+ _FindAVPixelFormat(format->format), // Target fmt
+ SWS_BICUBIC,
+ NULL, NULL, NULL);
+
+ if(video_dec->sws == NULL) {
+ Kit_SetError("Unable to initialize video converter context");
+ goto exit_3;
+ }
+
+ // Set callbacks and userdata, and we're go
+ video_dec->format = format;
+ dec->dec_decode = dec_decode_video_cb;
+ dec->dec_close = dec_close_video_cb;
+ dec->userdata = video_dec;
+ return dec;
+
+exit_3:
+ av_frame_free(&video_dec->scratch_frame);
+exit_2:
+ free(video_dec);
+exit_1:
+ Kit_CloseDecoder(dec);
+exit_0:
+ return NULL;
+}
+
+int Kit_GetVideoDecoderData(Kit_Decoder *dec, SDL_Texture *texture) {
+ assert(dec != NULL);
+
+ Kit_VideoPacket *packet = Kit_PeekDecoderOutput(dec);
+ if(packet == NULL) {
+ return 0;
+ }
+
+ Kit_VideoDecoder *video_dec = dec->userdata;
+ double sync_ts = _GetSystemTime() - dec->clock_sync;
+
+ // Check if we want the packet
+ if(packet->pts > sync_ts + VIDEO_SYNC_THRESHOLD) {
+ // Video is ahead, don't show yet.
+ return 0;
+ } else if(packet->pts < sync_ts - VIDEO_SYNC_THRESHOLD) {
+ // Video is lagging, skip until we find a good PTS to continue from.
+ while(packet != NULL) {
+ Kit_AdvanceDecoderOutput(dec);
+ free_out_video_packet_cb(packet);
+ packet = Kit_PeekDecoderOutput(dec);
+ if(packet == NULL) {
+ break;
+ } else {
+ dec->clock_pos = packet->pts;
+ }
+ if(packet->pts > sync_ts - VIDEO_SYNC_THRESHOLD) {
+ break;
}
}
- packet->size -= len;
- packet->data += len;
}
+
+ // If we have no viable packet, just skip
+ if(packet == NULL) {
+ return 0;
+ }
+
+ // Update output texture with current video data.
+ // Take formats into account.
+ switch(video_dec->format->format) {
+ case SDL_PIXELFORMAT_YV12:
+ case SDL_PIXELFORMAT_IYUV:
+ SDL_UpdateYUVTexture(
+ texture, NULL,
+ packet->frame->data[0], packet->frame->linesize[0],
+ packet->frame->data[1], packet->frame->linesize[1],
+ packet->frame->data[2], packet->frame->linesize[2]);
+ break;
+ default:
+ SDL_UpdateTexture(
+ texture, NULL,
+ packet->frame->data[0],
+ packet->frame->linesize[0]);
+ break;
+ }
+
+ // Advance buffer, and free the decoded frame.
+ Kit_AdvanceDecoderOutput(dec);
+ free_out_video_packet_cb(packet);
+ dec->clock_pos = packet->pts;
+
+ return 0;
}