mirror of
https://git.sr.ht/~thestr4ng3r/chiaki
synced 2025-08-20 21:43:12 -07:00
Move Android Video Input Buffer Handling to Thread
This commit is contained in:
parent
3a90ef0a65
commit
9d6c22551d
2 changed files with 109 additions and 44 deletions
|
@ -10,8 +10,9 @@
|
||||||
|
|
||||||
#include <string.h>
|
#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);
|
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)
|
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_width = target_width;
|
||||||
decoder->target_height = target_height;
|
decoder->target_height = target_height;
|
||||||
decoder->target_codec = codec;
|
decoder->target_codec = codec;
|
||||||
decoder->shutdown_output = false;
|
decoder->shutdown = false;
|
||||||
return chiaki_mutex_init(&decoder->codec_mutex, 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)
|
void android_chiaki_video_decoder_fini(AndroidChiakiVideoDecoder *decoder)
|
||||||
|
@ -31,22 +39,20 @@ void android_chiaki_video_decoder_fini(AndroidChiakiVideoDecoder *decoder)
|
||||||
if(decoder->codec)
|
if(decoder->codec)
|
||||||
{
|
{
|
||||||
chiaki_mutex_lock(&decoder->codec_mutex);
|
chiaki_mutex_lock(&decoder->codec_mutex);
|
||||||
decoder->shutdown_output = true;
|
decoder->shutdown = true;
|
||||||
ssize_t codec_buf_index = AMediaCodec_dequeueInputBuffer(decoder->codec, -1);
|
ssize_t codec_buf_index = AMediaCodec_dequeueInputBuffer(decoder->codec, INPUT_BUFFER_TIMEOUT_US);
|
||||||
if(codec_buf_index >= 0)
|
if(codec_buf_index >= 0)
|
||||||
{
|
{
|
||||||
CHIAKI_LOGI(decoder->log, "Video Decoder sending EOS buffer");
|
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_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
|
else
|
||||||
{
|
|
||||||
CHIAKI_LOGE(decoder->log, "Failed to get input buffer for shutting down Video Decoder!");
|
CHIAKI_LOGE(decoder->log, "Failed to get input buffer for shutting down Video Decoder!");
|
||||||
AMediaCodec_stop(decoder->codec);
|
AMediaCodec_stop(decoder->codec);
|
||||||
chiaki_mutex_unlock(&decoder->codec_mutex);
|
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);
|
AMediaCodec_delete(decoder->codec);
|
||||||
}
|
}
|
||||||
chiaki_mutex_fini(&decoder->codec_mutex);
|
chiaki_mutex_fini(&decoder->codec_mutex);
|
||||||
|
@ -103,15 +109,25 @@ void android_chiaki_video_decoder_set_surface(AndroidChiakiVideoDecoder *decoder
|
||||||
goto error_codec;
|
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)
|
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;
|
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;
|
goto beach;
|
||||||
|
|
||||||
|
error_input_thread:
|
||||||
|
decoder->shutdown = true;
|
||||||
|
|
||||||
error_codec:
|
error_codec:
|
||||||
AMediaCodec_delete(decoder->codec);
|
AMediaCodec_delete(decoder->codec);
|
||||||
decoder->codec = NULL;
|
decoder->codec = NULL;
|
||||||
|
@ -126,26 +142,62 @@ beach:
|
||||||
|
|
||||||
bool android_chiaki_video_decoder_video_sample(uint8_t *buf, size_t buf_size, void *user)
|
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;
|
AndroidChiakiVideoDecoder *decoder = user;
|
||||||
chiaki_mutex_lock(&decoder->codec_mutex);
|
chiaki_mutex_lock(&decoder->codec_mutex);
|
||||||
|
|
||||||
if(!decoder->codec)
|
if(!decoder->codec) // special case when init of the output thread fails but creating input thread succeeds
|
||||||
{
|
|
||||||
CHIAKI_LOGE(decoder->log, "Received video data, but decoder is not initialized!");
|
|
||||||
goto beach;
|
goto beach;
|
||||||
}
|
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
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++)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
chiaki_mutex_unlock(&decoder->codec_mutex); // unlock here because below code can block
|
||||||
|
uint8_t *buf = buf_start;
|
||||||
while(buf_size > 0)
|
while(buf_size > 0)
|
||||||
{
|
{
|
||||||
ssize_t codec_buf_index = AMediaCodec_dequeueInputBuffer(decoder->codec, INPUT_BUFFER_TIMEOUT_MS * 1000);
|
ssize_t codec_buf_index = AMediaCodec_dequeueInputBuffer(decoder->codec, INPUT_BUFFER_TIMEOUT_US);
|
||||||
if(codec_buf_index < 0)
|
if(codec_buf_index < 0)
|
||||||
{
|
{
|
||||||
CHIAKI_LOGE(decoder->log, "Failed to get input buffer");
|
CHIAKI_LOGE(decoder->log, "Failed to get input buffer: %d", (int)codec_buf_index);
|
||||||
r = false;
|
break;
|
||||||
goto beach;
|
// TODO: report somehow that there was a broken frame
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t codec_buf_size;
|
size_t codec_buf_size;
|
||||||
uint8_t *codec_buf = AMediaCodec_getInputBuffer(decoder->codec, (size_t)codec_buf_index, &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;
|
size_t codec_sample_size = buf_size;
|
||||||
|
@ -155,19 +207,23 @@ bool android_chiaki_video_decoder_video_sample(uint8_t *buf, size_t buf_size, vo
|
||||||
codec_sample_size = codec_buf_size;
|
codec_sample_size = codec_buf_size;
|
||||||
}
|
}
|
||||||
memcpy(codec_buf, buf, 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
|
media_status_t r = AMediaCodec_queueInputBuffer(decoder->codec, (size_t)codec_buf_index, 0, codec_sample_size, timestamp, 0);
|
||||||
if(r != AMEDIA_OK)
|
if(r != AMEDIA_OK)
|
||||||
{
|
{
|
||||||
CHIAKI_LOGE(decoder->log, "AMediaCodec_queueInputBuffer() failed: %d", (int)r);
|
CHIAKI_LOGE(decoder->log, "AMediaCodec_queueInputBuffer() failed: %d", (int)r);
|
||||||
|
// TODO: report here too
|
||||||
}
|
}
|
||||||
buf += codec_sample_size;
|
buf += codec_sample_size;
|
||||||
buf_size -= codec_sample_size;
|
buf_size -= codec_sample_size;
|
||||||
|
}
|
||||||
|
free(buf_start);
|
||||||
|
chiaki_mutex_lock(&decoder->codec_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
beach:
|
beach:
|
||||||
chiaki_mutex_unlock(&decoder->codec_mutex);
|
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)
|
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)
|
while(1)
|
||||||
{
|
{
|
||||||
AMediaCodecBufferInfo info;
|
AMediaCodecBufferInfo info;
|
||||||
ssize_t status = AMediaCodec_dequeueOutputBuffer(decoder->codec, &info, -1);
|
ssize_t status = AMediaCodec_dequeueOutputBuffer(decoder->codec, &info, 100000);
|
||||||
if(status >= 0)
|
if(status >= 0)
|
||||||
{
|
{
|
||||||
AMediaCodec_releaseOutputBuffer(decoder->codec, (size_t)status, info.size != 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
|
else
|
||||||
{
|
{
|
||||||
chiaki_mutex_lock(&decoder->codec_mutex);
|
chiaki_mutex_lock(&decoder->codec_mutex);
|
||||||
bool shutdown = decoder->shutdown_output;
|
bool shutdown = decoder->shutdown;
|
||||||
chiaki_mutex_unlock(&decoder->codec_mutex);
|
chiaki_mutex_unlock(&decoder->codec_mutex);
|
||||||
if(shutdown)
|
if(shutdown)
|
||||||
{
|
{
|
||||||
CHIAKI_LOGI(decoder->log, "Video Decoder Output Thread detected shutdown after reported error");
|
CHIAKI_LOGI(decoder->log, "Video Decoder Output Thread detected shutdown after reported error");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
else if(status != AMEDIACODEC_INFO_TRY_AGAIN_LATER)
|
||||||
|
CHIAKI_LOGE(decoder->log, "Video Decoder Output dequeue error: %d", (int)status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,15 +11,22 @@
|
||||||
typedef struct AMediaCodec AMediaCodec;
|
typedef struct AMediaCodec AMediaCodec;
|
||||||
typedef struct ANativeWindow ANativeWindow;
|
typedef struct ANativeWindow ANativeWindow;
|
||||||
|
|
||||||
|
#define ANDROID_CHIAKI_VIDEO_DECODER_FRAME_BUFFER_SIZE 4
|
||||||
|
|
||||||
typedef struct android_chiaki_video_decoder_t
|
typedef struct android_chiaki_video_decoder_t
|
||||||
{
|
{
|
||||||
ChiakiLog *log;
|
ChiakiLog *log;
|
||||||
ChiakiMutex codec_mutex;
|
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;
|
AMediaCodec *codec;
|
||||||
ANativeWindow *window;
|
ANativeWindow *window;
|
||||||
uint64_t timestamp_cur;
|
uint64_t timestamp_cur;
|
||||||
|
ChiakiThread input_thread;
|
||||||
ChiakiThread output_thread;
|
ChiakiThread output_thread;
|
||||||
bool shutdown_output;
|
bool shutdown;
|
||||||
int32_t target_width;
|
int32_t target_width;
|
||||||
int32_t target_height;
|
int32_t target_height;
|
||||||
ChiakiCodec target_codec;
|
ChiakiCodec target_codec;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue