diff options
-rw-r--r-- | examples/example_video.c | 10 | ||||
-rw-r--r-- | include/kitchensink/internal/kitbuffer.h | 2 | ||||
-rw-r--r-- | include/kitchensink/kitplayer.h | 6 | ||||
-rw-r--r-- | src/kitbuffer.c | 17 | ||||
-rw-r--r-- | src/kitplayer.c | 135 |
5 files changed, 154 insertions, 16 deletions
diff --git a/examples/example_video.c b/examples/example_video.c index 1afe957..84e726a 100644 --- a/examples/example_video.c +++ b/examples/example_video.c @@ -192,6 +192,15 @@ int main(int argc, char *argv[]) { if(event.key.keysym.sym == SDLK_ESCAPE) { run = false; } + if(event.key.keysym.sym == SDLK_q) { + Kit_PlayerPlay(player); + } + if(event.key.keysym.sym == SDLK_w) { + Kit_PlayerPause(player); + } + if(event.key.keysym.sym == SDLK_e) { + Kit_PlayerStop(player); + } break; case SDL_QUIT: run = false; @@ -217,6 +226,7 @@ int main(int argc, char *argv[]) { // Render to the screen SDL_RenderClear(renderer); SDL_RenderCopy(renderer, tex, NULL, NULL); + fprintf(stderr, "PRESENT\n"); SDL_RenderPresent(renderer); } diff --git a/include/kitchensink/internal/kitbuffer.h b/include/kitchensink/internal/kitbuffer.h index 772edf1..19a0c79 100644 --- a/include/kitchensink/internal/kitbuffer.h +++ b/include/kitchensink/internal/kitbuffer.h @@ -14,6 +14,8 @@ KIT_LOCAL Kit_Buffer* Kit_CreateBuffer(unsigned int size); KIT_LOCAL void Kit_DestroyBuffer(Kit_Buffer *buffer); KIT_LOCAL void* Kit_ReadBuffer(Kit_Buffer *buffer); +KIT_LOCAL void* Kit_PeekBuffer(const Kit_Buffer *buffer); +KIT_LOCAL void Kit_AdvanceBuffer(Kit_Buffer *buffer); KIT_LOCAL int Kit_WriteBuffer(Kit_Buffer *buffer, void *ptr); KIT_LOCAL int Kit_IsBufferFull(const Kit_Buffer *buffer); diff --git a/include/kitchensink/kitplayer.h b/include/kitchensink/kitplayer.h index 8f67a9b..a40b12d 100644 --- a/include/kitchensink/kitplayer.h +++ b/include/kitchensink/kitplayer.h @@ -15,8 +15,6 @@ extern "C" { #define KIT_CODECMAX 16 #define KIT_CODECNAMEMAX 128 -#define KIT_VBUFFERSIZE 3 -#define KIT_ABUFFERSIZE 65536 typedef enum Kit_PlayerState { KIT_STOPPED = 0, @@ -44,6 +42,10 @@ typedef struct Kit_Player { Kit_PlayerState state; Kit_VideoFormat vformat; Kit_AudioFormat aformat; + double clock_sync; + double clock_vprev; + double clock_aprev; + double pause_start; SDL_Thread *dec_thread; SDL_mutex *vmutex; SDL_mutex *amutex; diff --git a/src/kitbuffer.c b/src/kitbuffer.c index b9c2385..fe50c9a 100644 --- a/src/kitbuffer.c +++ b/src/kitbuffer.c @@ -31,6 +31,23 @@ void* Kit_ReadBuffer(Kit_Buffer *buffer) { return NULL; } +KIT_LOCAL void* Kit_PeekBuffer(const Kit_Buffer *buffer) { + assert(buffer != NULL); + return buffer->data[buffer->read_p % buffer->size]; +} + +KIT_LOCAL void Kit_AdvanceBuffer(Kit_Buffer *buffer) { + assert(buffer != NULL); + if(buffer->read_p < buffer->write_p) { + buffer->data[buffer->read_p % buffer->size] = NULL; + buffer->read_p++; + if(buffer->read_p >= buffer->size) { + buffer->read_p = buffer->read_p % buffer->size; + buffer->write_p = buffer->write_p % buffer->size; + } + } +} + int Kit_WriteBuffer(Kit_Buffer *buffer, void *ptr) { assert(buffer != NULL); assert(ptr != NULL); diff --git a/src/kitplayer.c b/src/kitplayer.c index 8849aab..111b5be 100644 --- a/src/kitplayer.c +++ b/src/kitplayer.c @@ -8,6 +8,7 @@ #include <libswscale/swscale.h> #include <libswresample/swresample.h> #include <libavutil/pixfmt.h> +#include <libavutil/time.h> #include <SDL2/SDL.h> @@ -15,6 +16,14 @@ #include <string.h> #include <assert.h> +// Threshold is in seconds +#define VIDEO_SYNC_THRESHOLD 0.01 + +// Buffersizes +#define KIT_VBUFFERSIZE 5 +#define KIT_ABUFFERSIZE (256*1024) +#define KIT_ABUFFERLIMIT (192*1024) + typedef struct Kit_VideoPacket { double pts; AVPicture *frame; @@ -58,7 +67,6 @@ static int _InitCodecs(Kit_Player *player, const Kit_Source *src) { // Make sure index seems correct if(src->vstream_idx >= (int)format_ctx->nb_streams) { - fprintf(stderr, "%d >= %d\n", src->vstream_idx, format_ctx->nb_streams); Kit_SetError("Invalid video stream index: %d", src->vstream_idx); goto exit_2; } else if(src->vstream_idx >= 0) { @@ -179,12 +187,17 @@ static Kit_VideoPacket* _CreateVideoPacket(AVPicture *frame, double pts) { return p; } +static double _GetSystemTime() { + return (double)av_gettime() / 1000000.0; +} + static void _HandleVideoPacket(Kit_Player *player, AVPacket *packet) { assert(player != NULL); assert(packet != NULL); int frame_finished; AVCodecContext *vcodec_ctx = (AVCodecContext*)player->vcodec_ctx; + AVFormatContext *fmt_ctx = (AVFormatContext *)player->src->format_ctx; AVPicture *iframe = (AVPicture*)player->tmp_vframe; avcodec_decode_video2(vcodec_ctx, (AVFrame*)player->tmp_vframe, &frame_finished, packet); @@ -208,8 +221,15 @@ static void _HandleVideoPacket(Kit_Player *player, AVPacket *packet) { oframe->data, oframe->linesize); + // Get pts + double pts = 0; + if(packet->dts != AV_NOPTS_VALUE) { + pts = av_frame_get_best_effort_timestamp(player->tmp_vframe); + pts *= av_q2d(fmt_ctx->streams[player->src->vstream_idx]->time_base); + } + // Lock, write to audio buffer, unlock - Kit_VideoPacket *vpacket = _CreateVideoPacket(oframe, 0); + Kit_VideoPacket *vpacket = _CreateVideoPacket(oframe, pts); if(SDL_LockMutex(player->vmutex) == 0) { Kit_WriteBuffer((Kit_Buffer*)player->vbuffer, vpacket); SDL_UnlockMutex(player->vmutex); @@ -295,7 +315,7 @@ static int _UpdatePlayer(Kit_Player *player) { if(player->acodec_ctx != NULL && SDL_LockMutex(player->amutex) == 0) { ret = Kit_GetRingBufferFree(player->abuffer); SDL_UnlockMutex(player->amutex); - if(ret < 16384) { + if(ret < KIT_ABUFFERLIMIT) { return 0; } } @@ -323,23 +343,37 @@ static int _UpdatePlayer(Kit_Player *player) { static int _DecoderThread(void *ptr) { Kit_Player *player = (Kit_Player*)ptr; - bool run = true; + bool is_running = true; + bool is_playing = true; int ret; - while(run) { - // If state is closed, bail. + while(is_running) { if(player->state == KIT_CLOSED) { - run = false; + is_running = false; continue; } + if(player->state == KIT_PLAYING) { + is_playing = true; + } + while(is_running && is_playing) { + if(player->state == KIT_CLOSED) { + is_running = false; + continue; + } + if(player->state == KIT_STOPPED) { + is_playing = false; + continue; + } - // Get more data from demuxer, decode. - ret = _UpdatePlayer(player); - if(ret == 1) { - run = false; - } else if(ret == 0) { - SDL_Delay(1); + // Get more data from demuxer, decode. + ret = _UpdatePlayer(player); + if(ret == 1) { + player->state = KIT_STOPPED; + } else if(ret == 0) { + SDL_Delay(1); + } } + SDL_Delay(5); } return 0; @@ -518,6 +552,14 @@ int Kit_RefreshTexture(Kit_Player *player, SDL_Texture *texture) { assert(player != NULL); assert(texture != NULL); + // If paused or stopped, do nothing + if(player->state == KIT_PAUSED) { + return 0; + } + if(player->state == KIT_STOPPED) { + return 0; + } + // Get texture information unsigned int format; int access, w, h; @@ -538,8 +580,9 @@ int Kit_RefreshTexture(Kit_Player *player, SDL_Texture *texture) { // 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_ReadBuffer((Kit_Buffer*)player->vbuffer); + packet = (Kit_VideoPacket*)Kit_PeekBuffer((Kit_Buffer*)player->vbuffer); SDL_UnlockMutex(player->vmutex); } else { return 1; @@ -548,6 +591,48 @@ int Kit_RefreshTexture(Kit_Player *player, SDL_Texture *texture) { return 0; } + // Print some data + double cur_video_ts = _GetSystemTime() - player->clock_sync; + fprintf(stderr, "clock %3.3f, pts %3.3f, diff %3.3f => ", + cur_video_ts, + packet->pts, + cur_video_ts - packet->pts); + + // Check if we want the packet + if(packet->pts > cur_video_ts + VIDEO_SYNC_THRESHOLD) { + fprintf(stderr, "WAIT\n"); + fflush(stderr); + return 0; + } else if(packet->pts < cur_video_ts - VIDEO_SYNC_THRESHOLD) { + fprintf(stderr, "SKIP\n"); + fflush(stderr); + if(SDL_LockMutex(player->vmutex) == 0) { + 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; + cur_video_ts = _GetSystemTime() - player->clock_sync; + player->clock_sync += cur_video_ts - packet->pts; + if(packet->pts > cur_video_ts - VIDEO_SYNC_THRESHOLD) { + break; + } + } + Kit_AdvanceBuffer((Kit_Buffer*)player->vbuffer); + SDL_UnlockMutex(player->vmutex); + } + } else { + fprintf(stderr, "SYNC\n"); + fflush(stderr); + if(SDL_LockMutex(player->vmutex) == 0) { + Kit_AdvanceBuffer((Kit_Buffer*)player->vbuffer); + SDL_UnlockMutex(player->vmutex); + } + } + // Update textures as required if(format == SDL_PIXELFORMAT_YV12 || format == SDL_PIXELFORMAT_IYUV) { SDL_UpdateYUVTexture( @@ -572,6 +657,15 @@ int Kit_GetAudioData(Kit_Player *player, unsigned char *buffer, size_t length) { assert(player != NULL); assert(buffer != NULL); + // If paused or stopped, do nothing + if(player->state == KIT_PAUSED) { + return 0; + } + if(player->state == KIT_STOPPED) { + return 0; + } + + // If asked for nothing, don't return anything either :P if(length == 0) { return 0; } @@ -611,10 +705,22 @@ KIT_API Kit_PlayerState Kit_GetPlayerState(const Kit_Player *player) { } KIT_API void Kit_PlayerPlay(Kit_Player *player) { + 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; + } player->state = KIT_PLAYING; } KIT_API void Kit_PlayerStop(Kit_Player *player) { + if(player->state == KIT_STOPPED) { + return; + } player->state = KIT_STOPPED; } @@ -622,5 +728,6 @@ KIT_API void Kit_PlayerPause(Kit_Player *player) { if(player->state != KIT_PLAYING) { return; } + player->pause_start = _GetSystemTime(); player->state = KIT_PAUSED; } |