diff options
author | Tuomas Virtanen <katajakasa@gmail.com> | 2017-09-22 15:14:58 +0300 |
---|---|---|
committer | Tuomas Virtanen <katajakasa@gmail.com> | 2017-09-22 15:17:21 +0300 |
commit | fdaaf05142e446f1047487763585c3a16801580c (patch) | |
tree | 8042ce6b2464a2bce4300c6a7b6a841209f24c6a | |
parent | fd7bbceb6321250ed5d80103d13e9d7584b552ac (diff) |
Split decoding to separate files
-rw-r--r-- | CMakeLists.txt | 41 | ||||
-rw-r--r-- | examples/example_video.c | 26 | ||||
-rw-r--r-- | include/kitchensink/internal/kitaudio.h | 22 | ||||
-rw-r--r-- | include/kitchensink/internal/kitcontrol.h | 24 | ||||
-rw-r--r-- | include/kitchensink/internal/kitdecoder.h | 54 | ||||
-rw-r--r-- | include/kitchensink/internal/kitlog.h | 11 | ||||
-rw-r--r-- | include/kitchensink/internal/kitsubtitle.h | 20 | ||||
-rw-r--r-- | include/kitchensink/internal/kitvideo.h | 18 | ||||
-rw-r--r-- | include/kitchensink/kitformats.h | 6 | ||||
-rw-r--r-- | include/kitchensink/kitplayer.h | 51 | ||||
-rw-r--r-- | include/kitchensink/kitsource.h | 8 | ||||
-rw-r--r-- | src/kitaudio.c | 247 | ||||
-rw-r--r-- | src/kitcontrol.c | 82 | ||||
-rw-r--r-- | src/kitdecoder.c | 220 | ||||
-rw-r--r-- | src/kitlib.c | 6 | ||||
-rw-r--r-- | src/kitplayer.c | 825 | ||||
-rw-r--r-- | src/kitsource.c | 18 | ||||
-rw-r--r-- | src/kitsubtitle.c | 234 | ||||
-rw-r--r-- | src/kitvideo.c | 258 |
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;
}
|