Move Android Video Input Buffer Handling to Thread

This commit is contained in:
Florian Märkl 2021-01-14 13:08:09 +01:00
parent 3a90ef0a65
commit 9d6c22551d
No known key found for this signature in database
GPG key ID: 125BC8A5A6A1E857
2 changed files with 109 additions and 44 deletions

View file

@ -10,8 +10,9 @@
#include <string.h>
#define INPUT_BUFFER_TIMEOUT_MS 10
#define INPUT_BUFFER_TIMEOUT_US 1000 * 1000
static void *android_chiaki_video_decoder_input_thread_func(void *user);
static void *android_chiaki_video_decoder_output_thread_func(void *user);
ChiakiErrorCode android_chiaki_video_decoder_init(AndroidChiakiVideoDecoder *decoder, ChiakiLog *log, int32_t target_width, int32_t target_height, ChiakiCodec codec)
@ -22,8 +23,15 @@ ChiakiErrorCode android_chiaki_video_decoder_init(AndroidChiakiVideoDecoder *dec
decoder->target_width = target_width;
decoder->target_height = target_height;
decoder->target_codec = codec;
decoder->shutdown_output = false;
return chiaki_mutex_init(&decoder->codec_mutex, false);
decoder->shutdown = false;
decoder->bufs_count = 0;
ChiakiErrorCode err = chiaki_mutex_init(&decoder->codec_mutex, false);
if(err != CHIAKI_ERR_SUCCESS)
return err;
err = chiaki_cond_init(&decoder->bufs_cond);
if(err != CHIAKI_ERR_SUCCESS)
chiaki_mutex_fini(&decoder->codec_mutex);
return err;
}
void android_chiaki_video_decoder_fini(AndroidChiakiVideoDecoder *decoder)
@ -31,22 +39,20 @@ void android_chiaki_video_decoder_fini(AndroidChiakiVideoDecoder *decoder)
if(decoder->codec)
{
chiaki_mutex_lock(&decoder->codec_mutex);
decoder->shutdown_output = true;
ssize_t codec_buf_index = AMediaCodec_dequeueInputBuffer(decoder->codec, -1);
decoder->shutdown = true;
ssize_t codec_buf_index = AMediaCodec_dequeueInputBuffer(decoder->codec, INPUT_BUFFER_TIMEOUT_US);
if(codec_buf_index >= 0)
{
CHIAKI_LOGI(decoder->log, "Video Decoder sending EOS buffer");
AMediaCodec_queueInputBuffer(decoder->codec, (size_t)codec_buf_index, 0, 0, decoder->timestamp_cur++, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
AMediaCodec_stop(decoder->codec);
chiaki_mutex_unlock(&decoder->codec_mutex);
chiaki_thread_join(&decoder->output_thread, NULL);
}
else
{
CHIAKI_LOGE(decoder->log, "Failed to get input buffer for shutting down Video Decoder!");
AMediaCodec_stop(decoder->codec);
chiaki_mutex_unlock(&decoder->codec_mutex);
}
AMediaCodec_stop(decoder->codec);
chiaki_mutex_unlock(&decoder->codec_mutex);
chiaki_cond_signal(&decoder->bufs_cond);
chiaki_thread_join(&decoder->output_thread, NULL);
chiaki_thread_join(&decoder->input_thread, NULL);
AMediaCodec_delete(decoder->codec);
}
chiaki_mutex_fini(&decoder->codec_mutex);
@ -103,15 +109,25 @@ void android_chiaki_video_decoder_set_surface(AndroidChiakiVideoDecoder *decoder
goto error_codec;
}
ChiakiErrorCode err = chiaki_thread_create(&decoder->output_thread, android_chiaki_video_decoder_output_thread_func, decoder);
ChiakiErrorCode err = chiaki_thread_create(&decoder->input_thread, android_chiaki_video_decoder_input_thread_func, decoder);
if(err != CHIAKI_ERR_SUCCESS)
{
CHIAKI_LOGE(decoder->log, "Failed to create output thread for AMediaCodec");
CHIAKI_LOGE(decoder->log, "Failed to create input thread for AMediaCodec");
goto error_codec;
}
err = chiaki_thread_create(&decoder->output_thread, android_chiaki_video_decoder_output_thread_func, decoder);
if(err != CHIAKI_ERR_SUCCESS)
{
CHIAKI_LOGE(decoder->log, "Failed to create output thread for AMediaCodec");
goto error_input_thread;
}
goto beach;
error_input_thread:
decoder->shutdown = true;
error_codec:
AMediaCodec_delete(decoder->codec);
decoder->codec = NULL;
@ -126,48 +142,88 @@ beach:
bool android_chiaki_video_decoder_video_sample(uint8_t *buf, size_t buf_size, void *user)
{
bool r = true;
bool r = false;
AndroidChiakiVideoDecoder *decoder = user;
chiaki_mutex_lock(&decoder->codec_mutex);
if(decoder->bufs_count >= ANDROID_CHIAKI_VIDEO_DECODER_FRAME_BUFFER_SIZE)
{
CHIAKI_LOGE(decoder->log, "All bufs full in video decoder");
goto beach;
}
uint8_t *buf_copy = malloc(buf_size);
if(!buf_copy)
goto beach;
memcpy(buf_copy, buf, buf_size);
decoder->bufs[decoder->bufs_count] = buf_copy;
decoder->bufs_sizes[decoder->bufs_count++] = buf_size;
chiaki_cond_signal(&decoder->bufs_cond);
r = true;
beach:
chiaki_mutex_unlock(&decoder->codec_mutex);
return r;
}
static void *android_chiaki_video_decoder_input_thread_func(void *user)
{
AndroidChiakiVideoDecoder *decoder = user;
chiaki_mutex_lock(&decoder->codec_mutex);
if(!decoder->codec)
{
CHIAKI_LOGE(decoder->log, "Received video data, but decoder is not initialized!");
if(!decoder->codec) // special case when init of the output thread fails but creating input thread succeeds
goto beach;
}
while(buf_size > 0)
while(1)
{
ssize_t codec_buf_index = AMediaCodec_dequeueInputBuffer(decoder->codec, INPUT_BUFFER_TIMEOUT_MS * 1000);
if(codec_buf_index < 0)
while(!decoder->shutdown && !decoder->bufs_count)
chiaki_cond_wait(&decoder->bufs_cond, &decoder->codec_mutex);
if(decoder->shutdown)
break;
uint8_t *buf_start = decoder->bufs[0];
size_t buf_size = decoder->bufs_sizes[0];
for(size_t i = 0; i < decoder->bufs_count - 1; i++)
{
CHIAKI_LOGE(decoder->log, "Failed to get input buffer");
r = false;
goto beach;
decoder->bufs[i] = decoder->bufs[i + 1];
decoder->bufs_sizes[i] = decoder->bufs_sizes[i + 1];
}
decoder->bufs_count--;
uint64_t timestamp = decoder->timestamp_cur++; // timestamp just raised by 1 for maximum realtime
size_t codec_buf_size;
uint8_t *codec_buf = AMediaCodec_getInputBuffer(decoder->codec, (size_t)codec_buf_index, &codec_buf_size);
size_t codec_sample_size = buf_size;
if(codec_sample_size > codec_buf_size)
chiaki_mutex_unlock(&decoder->codec_mutex); // unlock here because below code can block
uint8_t *buf = buf_start;
while(buf_size > 0)
{
//CHIAKI_LOGD(decoder->log, "Sample is bigger than buffer, splitting");
codec_sample_size = codec_buf_size;
ssize_t codec_buf_index = AMediaCodec_dequeueInputBuffer(decoder->codec, INPUT_BUFFER_TIMEOUT_US);
if(codec_buf_index < 0)
{
CHIAKI_LOGE(decoder->log, "Failed to get input buffer: %d", (int)codec_buf_index);
break;
// TODO: report somehow that there was a broken frame
}
size_t codec_buf_size;
uint8_t *codec_buf = AMediaCodec_getInputBuffer(decoder->codec, (size_t)codec_buf_index, &codec_buf_size);
size_t codec_sample_size = buf_size;
if(codec_sample_size > codec_buf_size)
{
//CHIAKI_LOGD(decoder->log, "Sample is bigger than buffer, splitting");
codec_sample_size = codec_buf_size;
}
memcpy(codec_buf, buf, codec_sample_size);
media_status_t r = AMediaCodec_queueInputBuffer(decoder->codec, (size_t)codec_buf_index, 0, codec_sample_size, timestamp, 0);
if(r != AMEDIA_OK)
{
CHIAKI_LOGE(decoder->log, "AMediaCodec_queueInputBuffer() failed: %d", (int)r);
// TODO: report here too
}
buf += codec_sample_size;
buf_size -= codec_sample_size;
}
memcpy(codec_buf, buf, codec_sample_size);
media_status_t r = AMediaCodec_queueInputBuffer(decoder->codec, (size_t)codec_buf_index, 0, codec_sample_size, decoder->timestamp_cur++, 0); // timestamp just raised by 1 for maximum realtime
if(r != AMEDIA_OK)
{
CHIAKI_LOGE(decoder->log, "AMediaCodec_queueInputBuffer() failed: %d", (int)r);
}
buf += codec_sample_size;
buf_size -= codec_sample_size;
free(buf_start);
chiaki_mutex_lock(&decoder->codec_mutex);
}
beach:
chiaki_mutex_unlock(&decoder->codec_mutex);
return r;
CHIAKI_LOGI(decoder->log, "Video Decoder Input Thread exiting");
return NULL;
}
static void *android_chiaki_video_decoder_output_thread_func(void *user)
@ -177,7 +233,7 @@ static void *android_chiaki_video_decoder_output_thread_func(void *user)
while(1)
{
AMediaCodecBufferInfo info;
ssize_t status = AMediaCodec_dequeueOutputBuffer(decoder->codec, &info, -1);
ssize_t status = AMediaCodec_dequeueOutputBuffer(decoder->codec, &info, 100000);
if(status >= 0)
{
AMediaCodec_releaseOutputBuffer(decoder->codec, (size_t)status, info.size != 0);
@ -190,13 +246,15 @@ static void *android_chiaki_video_decoder_output_thread_func(void *user)
else
{
chiaki_mutex_lock(&decoder->codec_mutex);
bool shutdown = decoder->shutdown_output;
bool shutdown = decoder->shutdown;
chiaki_mutex_unlock(&decoder->codec_mutex);
if(shutdown)
{
CHIAKI_LOGI(decoder->log, "Video Decoder Output Thread detected shutdown after reported error");
break;
}
else if(status != AMEDIACODEC_INFO_TRY_AGAIN_LATER)
CHIAKI_LOGE(decoder->log, "Video Decoder Output dequeue error: %d", (int)status);
}
}

View file

@ -11,15 +11,22 @@
typedef struct AMediaCodec AMediaCodec;
typedef struct ANativeWindow ANativeWindow;
#define ANDROID_CHIAKI_VIDEO_DECODER_FRAME_BUFFER_SIZE 4
typedef struct android_chiaki_video_decoder_t
{
ChiakiLog *log;
ChiakiMutex codec_mutex;
uint8_t *bufs[ANDROID_CHIAKI_VIDEO_DECODER_FRAME_BUFFER_SIZE];
size_t bufs_sizes[ANDROID_CHIAKI_VIDEO_DECODER_FRAME_BUFFER_SIZE];
size_t bufs_count;
ChiakiCond bufs_cond;
AMediaCodec *codec;
ANativeWindow *window;
uint64_t timestamp_cur;
ChiakiThread input_thread;
ChiakiThread output_thread;
bool shutdown_output;
bool shutdown;
int32_t target_width;
int32_t target_height;
ChiakiCodec target_codec;