From fb8b8f3b6b8b1905a3811ef3a6c396d0f3dc26a6 Mon Sep 17 00:00:00 2001 From: Tuomas Virtanen Date: Thu, 14 Jan 2016 04:22:02 +0200 Subject: Add seeking --- src/kitplayer.c | 444 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 315 insertions(+), 129 deletions(-) (limited to 'src/kitplayer.c') diff --git a/src/kitplayer.c b/src/kitplayer.c index d4e23fc..f022e1e 100644 --- a/src/kitplayer.c +++ b/src/kitplayer.c @@ -17,6 +17,8 @@ #include #include #include +#define __STDC_FORMAT_MACROS +#include // Threshold is in seconds #define VIDEO_SYNC_THRESHOLD 0.01 @@ -25,6 +27,12 @@ // Buffersizes #define KIT_VBUFFERSIZE 3 #define KIT_ABUFFERSIZE 64 +#define KIT_CBUFFERSIZE 8 + +typedef enum Kit_ControlPacketType { + KIT_CONTROL_SEEK, + KIT_CONTROL_FLUSH +} Kit_ControlPacketType; typedef struct Kit_VideoPacket { double pts; @@ -37,6 +45,11 @@ typedef struct Kit_AudioPacket { Kit_RingBuffer *rb; } Kit_AudioPacket; +typedef struct Kit_ControlPacket { + Kit_ControlPacketType type; + double value1; +} Kit_ControlPacket; + static int _InitCodecs(Kit_Player *player, const Kit_Source *src) { assert(player != NULL); assert(src != NULL); @@ -212,6 +225,40 @@ static Kit_AudioPacket* _CreateAudioPacket(const char* data, size_t len, double return p; } +static void _FreeControlPacket(Kit_ControlPacket *packet) { + free(packet); +} + +static Kit_ControlPacket* _CreateControlPacket(Kit_ControlPacketType type, double value1) { + Kit_ControlPacket *p = calloc(1, sizeof(Kit_ControlPacket)); + p->type = type; + p->value1 = value1; + return p; +} + +static void _FlushVideoBuffer(Kit_Player *player) { + Kit_VideoPacket *vp; + while((vp = Kit_ReadBuffer(player->vbuffer)) != NULL) { + _FreeVideoPacket(vp); + } + fflush(stderr); +} + +static void _FlushAudioBuffer(Kit_Player *player) { + Kit_AudioPacket *ap; + while((ap = Kit_ReadBuffer(player->abuffer)) != NULL) { + _FreeAudioPacket(ap); + } + fflush(stderr); +} + +static void _FlushControlBuffer(Kit_Player *player) { + Kit_ControlPacket *cp; + while((cp = Kit_ReadBuffer(player->cbuffer)) != NULL) { + _FreeControlPacket(cp); + } +} + static double _GetSystemTime() { return (double)av_gettime() / 1000000.0; } @@ -253,6 +300,13 @@ static void _HandleVideoPacket(Kit_Player *player, AVPacket *packet) { pts *= av_q2d(fmt_ctx->streams[player->src->vstream_idx]->time_base); } + // Just seeked, set sync clock & pos. + if(player->seek_flag == 1) { + player->vclock_pos = pts; + player->clock_sync = _GetSystemTime() - pts; + player->seek_flag = 0; + } + // Lock, write to audio buffer, unlock Kit_VideoPacket *vpacket = _CreateVideoPacket(oframe, pts); if(SDL_LockMutex(player->vmutex) == 0) { @@ -317,6 +371,13 @@ static void _HandleAudioPacket(Kit_Player *player, AVPacket *packet) { pts *= av_q2d(fmt_ctx->streams[player->src->astream_idx]->time_base); } + // Just seeked, set sync clock & pos. + if(player->seek_flag == 1) { + player->vclock_pos = pts; + player->clock_sync = _GetSystemTime() - pts; + player->seek_flag = 0; + } + // Lock, write to audio buffer, unlock Kit_AudioPacket *apacket = _CreateAudioPacket((char*)dst_data[0], (size_t)dst_bufsize, pts); if(SDL_LockMutex(player->amutex) == 0) { @@ -330,47 +391,110 @@ static void _HandleAudioPacket(Kit_Player *player, AVPacket *packet) { } } +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); + } + if(player->acodec_ctx != NULL && packet->stream_index == player->src->astream_idx) { + _HandleAudioPacket(player, packet); + } +} + +static void _HandleFlushCommand(Kit_Player *player, Kit_ControlPacket *packet) { + if(SDL_LockMutex(player->amutex) == 0) { + _FlushAudioBuffer(player); + SDL_UnlockMutex(player->amutex); + } + if(SDL_LockMutex(player->vmutex) == 0) { + _FlushVideoBuffer(player); + SDL_UnlockMutex(player->vmutex); + } +} + +static void _HandleSeekCommand(Kit_Player *player, Kit_ControlPacket *packet) { + AVFormatContext *fmt_ctx = (AVFormatContext *)player->src->format_ctx; + + // Find and limit absolute position + double seek = packet->value1; + double duration = Kit_GetPlayerDuration(player); + if(player->vclock_pos + seek <= 0) { + seek = -player->vclock_pos; + } + if(player->vclock_pos + seek >= duration) { + seek = duration - player->vclock_pos; + } + double absolute_pos = player->vclock_pos + seek; + int64_t seek_target = absolute_pos * AV_TIME_BASE; + + // Seek to timestamp. + avformat_seek_file(fmt_ctx, -1, INT64_MIN, seek_target, INT64_MAX, 0); + if(player->vcodec_ctx != NULL) + avcodec_flush_buffers(player->vcodec_ctx); + if(player->acodec_ctx != NULL) + avcodec_flush_buffers(player->acodec_ctx); + + // On first packet, set clock and current position + player->seek_flag = 1; +} + +static void _HandleControlPacket(Kit_Player *player, Kit_ControlPacket *packet) { + switch(packet->type) { + case KIT_CONTROL_FLUSH: + _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) { assert(player != NULL); - int ret; + 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); + } // If either buffer is full, just stop here for now. - if(player->vcodec_ctx != NULL && SDL_LockMutex(player->vmutex) == 0) { - ret = Kit_IsBufferFull(player->vbuffer); - SDL_UnlockMutex(player->vmutex); - if(ret) { - return 0; + // 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(player->acodec_ctx != NULL && SDL_LockMutex(player->amutex) == 0) { - ret = Kit_IsBufferFull(player->abuffer); - SDL_UnlockMutex(player->amutex); - if(ret) { - 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; + } } } - AVPacket packet; - AVFormatContext *format_ctx = (AVFormatContext*)player->src->format_ctx; - // Attempt to read frame. Just return here if it fails. + AVPacket packet; if(av_read_frame(format_ctx, &packet) < 0) { return 1; } - - // 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); - } - if(player->acodec_ctx != NULL && packet.stream_index == player->src->astream_idx) { - _HandleAudioPacket(player, &packet); - } - - // Free packet and that's that + _HandlePacket(player, &packet); av_free_packet(&packet); return -1; } @@ -399,7 +523,7 @@ static int _DecoderThread(void *ptr) { continue; } - // Get more data from demuxer, decode. + // 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; @@ -407,6 +531,8 @@ static int _DecoderThread(void *ptr) { SDL_Delay(1); } } + + // Just idle while waiting for work. SDL_Delay(10); } @@ -500,6 +626,12 @@ Kit_Player* Kit_CreatePlayer(const Kit_Source *src) { } } + player->cbuffer = Kit_CreateBuffer(KIT_CBUFFERSIZE); + if(player->cbuffer == NULL) { + Kit_SetError("Unable to initialize control ringbuffer"); + goto error; + } + player->vmutex = SDL_CreateMutex(); if(player->vmutex == NULL) { Kit_SetError("Unable to allocate video mutex"); @@ -512,6 +644,12 @@ Kit_Player* Kit_CreatePlayer(const Kit_Source *src) { goto error; } + player->cmutex = SDL_CreateMutex(); + if(player->cmutex == NULL) { + Kit_SetError("Unable to allocate control buffer mutex"); + goto error; + } + 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()); @@ -527,6 +665,9 @@ error: if(player->vmutex != NULL) { SDL_DestroyMutex(player->vmutex); } + if(player->cmutex != NULL) { + SDL_DestroyMutex(player->cmutex); + } if(player->tmp_aframe != NULL) { av_frame_free((AVFrame**)&player->tmp_aframe); } @@ -539,6 +680,9 @@ error: if(player->abuffer != NULL) { Kit_DestroyBuffer((Kit_Buffer*)player->abuffer); } + if(player->cbuffer != NULL) { + Kit_DestroyBuffer((Kit_Buffer*)player->cbuffer); + } if(player->sws != NULL) { sws_freeContext((struct SwsContext *)player->sws); } @@ -559,6 +703,7 @@ void Kit_ClosePlayer(Kit_Player *player) { SDL_WaitThread(player->dec_thread, NULL); SDL_DestroyMutex(player->vmutex); SDL_DestroyMutex(player->amutex); + SDL_DestroyMutex(player->cmutex); // Free up the ffmpeg context if(player->sws != NULL) { @@ -578,21 +723,21 @@ void Kit_ClosePlayer(Kit_Player *player) { avcodec_free_context((AVCodecContext**)&player->acodec_ctx); avcodec_free_context((AVCodecContext**)&player->vcodec_ctx); + // Free local audio buffers + if(player->cbuffer != NULL) { + _FlushControlBuffer(player); + Kit_DestroyBuffer((Kit_Buffer*)player->cbuffer); + } + // Free local audio buffers if(player->abuffer != NULL) { - Kit_AudioPacket *ap; - while((ap = Kit_ReadBuffer(player->abuffer)) != NULL) { - _FreeAudioPacket(ap); - } + _FlushAudioBuffer(player); Kit_DestroyBuffer((Kit_Buffer*)player->abuffer); } // Free local video buffers if(player->vbuffer != NULL) { - Kit_VideoPacket *vp; - while((vp = Kit_ReadBuffer(player->vbuffer)) != NULL) { - _FreeVideoPacket(vp); - } + _FlushVideoBuffer(player); Kit_DestroyBuffer((Kit_Buffer*)player->vbuffer); } @@ -622,24 +767,21 @@ int Kit_RefreshTexture(Kit_Player *player, SDL_Texture *texture) { Kit_VideoPacket *n_packet = NULL; if(SDL_LockMutex(player->vmutex) == 0) { packet = (Kit_VideoPacket*)Kit_PeekBuffer((Kit_Buffer*)player->vbuffer); - SDL_UnlockMutex(player->vmutex); - } else { - return 1; - } - if(packet == NULL) { - return 0; - } + if(packet == NULL) { + SDL_UnlockMutex(player->vmutex); + return 0; + } - // Print some data - double cur_video_ts = _GetSystemTime() - player->clock_sync; + // 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. - 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. - if(SDL_LockMutex(player->vmutex) == 0) { + // 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); @@ -652,45 +794,52 @@ int Kit_RefreshTexture(Kit_Player *player, SDL_Texture *texture) { break; } } - SDL_UnlockMutex(player->vmutex); } - } - // Advance buffer one frame forwards - if(SDL_LockMutex(player->vmutex) == 0) { + // 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; } - // 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]); - } - - // Free the packet - _FreeVideoPacket(packet); return 0; } -int Kit_GetAudioData(Kit_Player *player, unsigned char *buffer, size_t length, size_t cur_buf_len) { +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->astream_idx == -1) { 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 @@ -701,76 +850,75 @@ int Kit_GetAudioData(Kit_Player *player, unsigned char *buffer, size_t length, s return 0; } - // If asked for nothing, don't return anything either :P - if(length == 0) { - 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); - SDL_UnlockMutex(player->amutex); - } else { - return 1; - } - if(packet == NULL) { - 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 = diff * player->aformat.samplerate; - - if(packet->pts > cur_audio_ts + AUDIO_SYNC_THRESHOLD) { - // Audio is ahead, fill buffer with some silence + if(packet == NULL) { + SDL_UnlockMutex(player->amutex); + return 0; + } - int max_diff_samples = length / bytes_per_sample; - int max_samples = (max_diff_samples < diff_samples) ? max_diff_samples : diff_samples; + 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)); - av_samples_set_silence( - &buffer, - 0, // Offset - max_samples, - player->aformat.channels, - _FindAVSampleFormat(player->aformat.format)); + int diff_bytes = max_samples * bytes_per_sample; - int diff_bytes = max_samples * bytes_per_sample; - buffer += diff_bytes; - length -= diff_bytes; + 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 + } 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; + 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; + } } } - } - int ret = Kit_ReadRingBuffer(packet->rb, (char*)buffer, length); - if(Kit_GetRingBufferLength(packet->rb) == 0) { - if(SDL_LockMutex(player->amutex) == 0) { + if(length > 0) { + ret += Kit_ReadRingBuffer(packet->rb, (char*)buffer, length); + } + + if(Kit_GetRingBufferLength(packet->rb) == 0) { Kit_AdvanceBuffer((Kit_Buffer*)player->abuffer); - SDL_UnlockMutex(player->amutex); + _FreeAudioPacket(packet); + } else { + double adjust = (double)ret / bps; + packet->pts += adjust; } + + SDL_UnlockMutex(player->amutex); } else { - double adjust = (double)ret / bps; - packet->pts += adjust; + Kit_SetError("Unable to lock audio buffer mutex"); + ret = 1; } - _FreeAudioPacket(packet); return ret; } @@ -796,11 +944,15 @@ void Kit_GetPlayerInfo(const Kit_Player *player, Kit_PlayerInfo *info) { } } -KIT_API Kit_PlayerState Kit_GetPlayerState(const Kit_Player *player) { +Kit_PlayerState Kit_GetPlayerState(const Kit_Player *player) { + assert(player != NULL); + return player->state; } -KIT_API void Kit_PlayerPlay(Kit_Player *player) { +void Kit_PlayerPlay(Kit_Player *player) { + assert(player != NULL); + if(player->state == KIT_PLAYING) { return; } @@ -813,17 +965,51 @@ KIT_API void Kit_PlayerPlay(Kit_Player *player) { player->state = KIT_PLAYING; } -KIT_API void Kit_PlayerStop(Kit_Player *player) { +void Kit_PlayerStop(Kit_Player *player) { + assert(player != NULL); + if(player->state == KIT_STOPPED) { return; } player->state = KIT_STOPPED; } -KIT_API void Kit_PlayerPause(Kit_Player *player) { +void Kit_PlayerPause(Kit_Player *player) { + assert(player != NULL); + if(player->state != KIT_PLAYING) { return; } player->pause_start = _GetSystemTime(); player->state = KIT_PAUSED; } + +int Kit_PlayerSeek(Kit_Player *player, double m_time) { + assert(player != NULL); + + // 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; + } + + return 0; +} + +double Kit_GetPlayerDuration(const Kit_Player *player) { + assert(player != NULL); + + AVFormatContext *fmt_ctx = (AVFormatContext *)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; +} -- cgit v1.2.3