fix(ffmpeg): add native filesystem API support for FFmpeg image decoder (#7253)

This commit is contained in:
lxydiy 2024-11-20 18:57:45 +08:00 committed by GitHub
parent 11be75e302
commit 4a77a05fb3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 124 additions and 18 deletions

View File

@ -1384,6 +1384,13 @@ menu "LVGL configuration"
bool "Dump format" bool "Dump format"
depends on LV_USE_FFMPEG depends on LV_USE_FFMPEG
default n default n
config LV_FFMPEG_PLAYER_USE_LV_FS
bool "Use lvgl file path in FFmpeg Player widget"
depends on LV_USE_FFMPEG
default n
help
You won't be able to open URLs after enabling this feature.
Note that FFmpeg image decoder will always use lvgl file system.
endmenu endmenu
menu "Others" menu "Others"

View File

@ -33,9 +33,7 @@ Enable :c:macro:`LV_USE_FFMPEG` in ``lv_conf.h``.
See the examples below. See the examples below.
:note: FFmpeg extension doesn't use LVGL's file system. You can :note: Enable :c:macro:`LV_FFMPEG_PLAYER_USE_LV_FS` in ``lv_conf.h`` if you want to integrate the lvgl file system into FFmpeg.
simply pass the path to the image or video as usual on your operating
system or platform.
.. _ffmpeg_example: .. _ffmpeg_example:

View File

@ -932,6 +932,10 @@
#if LV_USE_FFMPEG #if LV_USE_FFMPEG
/** Dump input information to stderr */ /** Dump input information to stderr */
#define LV_FFMPEG_DUMP_FORMAT 0 #define LV_FFMPEG_DUMP_FORMAT 0
/** Use lvgl file path in FFmpeg Player widget
* You won't be able to open URLs after enabling this feature.
* Note that FFmpeg image decoder will always use lvgl file system. */
#define LV_FFMPEG_PLAYER_USE_LV_FS 0
#endif #endif
/*================== /*==================

View File

@ -38,10 +38,14 @@
#define FRAME_DEF_REFR_PERIOD 33 /*[ms]*/ #define FRAME_DEF_REFR_PERIOD 33 /*[ms]*/
#define DECODER_BUFFER_SIZE (8 * 1024)
/********************** /**********************
* TYPEDEFS * TYPEDEFS
**********************/ **********************/
struct ffmpeg_context_s { struct ffmpeg_context_s {
AVIOContext * io_ctx;
lv_fs_file_t lv_file;
AVFormatContext * fmt_ctx; AVFormatContext * fmt_ctx;
AVCodecContext * video_dec_ctx; AVCodecContext * video_dec_ctx;
AVStream * video_stream; AVStream * video_stream;
@ -75,12 +79,15 @@ static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_d
static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc); static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_dsc_t * dsc);
static void decoder_close(lv_image_decoder_t * dec, lv_image_decoder_dsc_t * dsc); static void decoder_close(lv_image_decoder_t * dec, lv_image_decoder_dsc_t * dsc);
static struct ffmpeg_context_s * ffmpeg_open_file(const char * path); static int ffmpeg_lvfs_read(void * ptr, uint8_t * buf, int buf_size);
static int64_t ffmpeg_lvfs_seek(void * ptr, int64_t pos, int whence);
static AVIOContext * ffmpeg_open_io_context(lv_fs_file_t * file);
static struct ffmpeg_context_s * ffmpeg_open_file(const char * path, bool is_lv_fs_path);
static void ffmpeg_close(struct ffmpeg_context_s * ffmpeg_ctx); static void ffmpeg_close(struct ffmpeg_context_s * ffmpeg_ctx);
static void ffmpeg_close_src_ctx(struct ffmpeg_context_s * ffmpeg_ctx); static void ffmpeg_close_src_ctx(struct ffmpeg_context_s * ffmpeg_ctx);
static void ffmpeg_close_dst_ctx(struct ffmpeg_context_s * ffmpeg_ctx); static void ffmpeg_close_dst_ctx(struct ffmpeg_context_s * ffmpeg_ctx);
static int ffmpeg_image_allocate(struct ffmpeg_context_s * ffmpeg_ctx); static int ffmpeg_image_allocate(struct ffmpeg_context_s * ffmpeg_ctx);
static int ffmpeg_get_image_header(const char * path, lv_image_header_t * header); static int ffmpeg_get_image_header(lv_image_decoder_dsc_t * dsc, lv_image_header_t * header);
static int ffmpeg_get_frame_refr_period(struct ffmpeg_context_s * ffmpeg_ctx); static int ffmpeg_get_frame_refr_period(struct ffmpeg_context_s * ffmpeg_ctx);
static uint8_t * ffmpeg_get_image_data(struct ffmpeg_context_s * ffmpeg_ctx); static uint8_t * ffmpeg_get_image_data(struct ffmpeg_context_s * ffmpeg_ctx);
static int ffmpeg_update_next_frame(struct ffmpeg_context_s * ffmpeg_ctx); static int ffmpeg_update_next_frame(struct ffmpeg_context_s * ffmpeg_ctx);
@ -128,7 +135,7 @@ void lv_ffmpeg_init(void)
int lv_ffmpeg_get_frame_num(const char * path) int lv_ffmpeg_get_frame_num(const char * path)
{ {
int ret = -1; int ret = -1;
struct ffmpeg_context_s * ffmpeg_ctx = ffmpeg_open_file(path); struct ffmpeg_context_s * ffmpeg_ctx = ffmpeg_open_file(path, LV_FFMPEG_PLAYER_USE_LV_FS);
if(ffmpeg_ctx) { if(ffmpeg_ctx) {
ret = ffmpeg_ctx->video_stream->nb_frames; ret = ffmpeg_ctx->video_stream->nb_frames;
@ -159,7 +166,7 @@ lv_result_t lv_ffmpeg_player_set_src(lv_obj_t * obj, const char * path)
lv_timer_pause(player->timer); lv_timer_pause(player->timer);
player->ffmpeg_ctx = ffmpeg_open_file(path); player->ffmpeg_ctx = ffmpeg_open_file(path, LV_FFMPEG_PLAYER_USE_LV_FS);
if(!player->ffmpeg_ctx) { if(!player->ffmpeg_ctx) {
LV_LOG_ERROR("ffmpeg file open failed: %s", path); LV_LOG_ERROR("ffmpeg file open failed: %s", path);
@ -260,13 +267,10 @@ static lv_result_t decoder_info(lv_image_decoder_t * decoder, lv_image_decoder_d
LV_UNUSED(decoder); LV_UNUSED(decoder);
/* Get the source type */ /* Get the source type */
const void * src = dsc->src;
lv_image_src_t src_type = dsc->src_type; lv_image_src_t src_type = dsc->src_type;
if(src_type == LV_IMAGE_SRC_FILE) { if(src_type == LV_IMAGE_SRC_FILE) {
const char * fn = src; if(ffmpeg_get_image_header(dsc, header) < 0) {
if(ffmpeg_get_image_header(fn, header) < 0) {
LV_LOG_ERROR("ffmpeg can't get image header"); LV_LOG_ERROR("ffmpeg can't get image header");
return LV_RESULT_INVALID; return LV_RESULT_INVALID;
} }
@ -291,7 +295,7 @@ static lv_result_t decoder_open(lv_image_decoder_t * decoder, lv_image_decoder_d
if(dsc->src_type == LV_IMAGE_SRC_FILE) { if(dsc->src_type == LV_IMAGE_SRC_FILE) {
const char * path = dsc->src; const char * path = dsc->src;
struct ffmpeg_context_s * ffmpeg_ctx = ffmpeg_open_file(path); struct ffmpeg_context_s * ffmpeg_ctx = ffmpeg_open_file(path, true);
if(ffmpeg_ctx == NULL) { if(ffmpeg_ctx == NULL) {
return LV_RESULT_INVALID; return LV_RESULT_INVALID;
@ -564,18 +568,33 @@ static int ffmpeg_open_codec_context(int * stream_idx,
return 0; return 0;
} }
static int ffmpeg_get_image_header(const char * filepath, static int ffmpeg_get_image_header(lv_image_decoder_dsc_t * dsc,
lv_image_header_t * header) lv_image_header_t * header)
{ {
int ret = -1; int ret = -1;
AVFormatContext * fmt_ctx = NULL; AVFormatContext * fmt_ctx = NULL;
AVCodecContext * video_dec_ctx = NULL; AVCodecContext * video_dec_ctx = NULL;
AVIOContext * io_ctx = NULL;
int video_stream_idx; int video_stream_idx;
io_ctx = ffmpeg_open_io_context(&dsc->file);
if(io_ctx == NULL) {
LV_LOG_ERROR("io_ctx malloc failed");
return ret;
}
fmt_ctx = avformat_alloc_context();
if(fmt_ctx == NULL) {
LV_LOG_ERROR("fmt_ctx malloc failed");
goto failed;
}
fmt_ctx->pb = io_ctx;
fmt_ctx->flags |= AVFMT_FLAG_CUSTOM_IO;
/* open input file, and allocate format context */ /* open input file, and allocate format context */
if(avformat_open_input(&fmt_ctx, filepath, NULL, NULL) < 0) { if(avformat_open_input(&fmt_ctx, (const char *)dsc->src, NULL, NULL) < 0) {
LV_LOG_ERROR("Could not open source file %s", filepath); LV_LOG_ERROR("Could not open source file %s", (const char *)dsc->src);
goto failed; goto failed;
} }
@ -602,7 +621,10 @@ static int ffmpeg_get_image_header(const char * filepath,
failed: failed:
avcodec_free_context(&video_dec_ctx); avcodec_free_context(&video_dec_ctx);
avformat_close_input(&fmt_ctx); avformat_close_input(&fmt_ctx);
if(io_ctx != NULL) {
av_free(io_ctx->buffer);
av_free(io_ctx);
}
return ret; return ret;
} }
@ -658,7 +680,48 @@ static int ffmpeg_update_next_frame(struct ffmpeg_context_s * ffmpeg_ctx)
return ret; return ret;
} }
struct ffmpeg_context_s * ffmpeg_open_file(const char * path) static int ffmpeg_lvfs_read(void * ptr, uint8_t * buf, int buf_size)
{
lv_fs_file_t * file = ptr;
uint32_t bytesRead = 0;
lv_fs_res_t res = lv_fs_read(file, buf, buf_size, &bytesRead);
if(bytesRead == 0)
return AVERROR_EOF; /* Let FFmpeg know that we have reached eof */
if(res != LV_FS_RES_OK)
return AVERROR_EOF;
return bytesRead;
}
static int64_t ffmpeg_lvfs_seek(void * ptr, int64_t pos, int whence)
{
lv_fs_file_t * file = ptr;
if(whence == SEEK_SET && lv_fs_seek(file, pos, SEEK_SET) == LV_FS_RES_OK) {
return pos;
}
return -1;
}
static AVIOContext * ffmpeg_open_io_context(lv_fs_file_t * file)
{
uint8_t * iBuffer = av_malloc(DECODER_BUFFER_SIZE);
if(iBuffer == NULL) {
LV_LOG_ERROR("iBuffer malloc failed");
return NULL;
}
AVIOContext * pIOCtx = avio_alloc_context(iBuffer, DECODER_BUFFER_SIZE, /* internal Buffer and its size */
0, /* bWriteable (1=true,0=false) */
file, /* user data ; will be passed to our callback functions */
ffmpeg_lvfs_read, /* Read callback function */
0, /* Write callback function */
ffmpeg_lvfs_seek); /* Seek callback function */
if(pIOCtx == NULL) {
av_free(iBuffer);
return NULL;
}
return pIOCtx;
}
static struct ffmpeg_context_s * ffmpeg_open_file(const char * path, bool is_lv_fs_path)
{ {
if(path == NULL || lv_strlen(path) == 0) { if(path == NULL || lv_strlen(path) == 0) {
LV_LOG_ERROR("file path is empty"); LV_LOG_ERROR("file path is empty");
@ -672,6 +735,25 @@ struct ffmpeg_context_s * ffmpeg_open_file(const char * path)
goto failed; goto failed;
} }
if(is_lv_fs_path) {
lv_fs_open(&(ffmpeg_ctx->lv_file), path, LV_FS_MODE_RD); /* image_decoder_get_info says the file truly exists. */
ffmpeg_ctx->io_ctx = ffmpeg_open_io_context(&(ffmpeg_ctx->lv_file)); /* Save the buffer pointer to free it later */
if(ffmpeg_ctx->io_ctx == NULL) {
LV_LOG_ERROR("io_ctx malloc failed");
goto failed;
}
ffmpeg_ctx->fmt_ctx = avformat_alloc_context();
if(ffmpeg_ctx->fmt_ctx == NULL) {
LV_LOG_ERROR("fmt_ctx malloc failed");
goto failed;
}
ffmpeg_ctx->fmt_ctx->pb = ffmpeg_ctx->io_ctx;
ffmpeg_ctx->fmt_ctx->flags |= AVFMT_FLAG_CUSTOM_IO;
}
/* open input file, and allocate format context */ /* open input file, and allocate format context */
if(avformat_open_input(&(ffmpeg_ctx->fmt_ctx), path, NULL, NULL) < 0) { if(avformat_open_input(&(ffmpeg_ctx->fmt_ctx), path, NULL, NULL) < 0) {
@ -799,6 +881,11 @@ static void ffmpeg_close(struct ffmpeg_context_s * ffmpeg_ctx)
sws_freeContext(ffmpeg_ctx->sws_ctx); sws_freeContext(ffmpeg_ctx->sws_ctx);
ffmpeg_close_src_ctx(ffmpeg_ctx); ffmpeg_close_src_ctx(ffmpeg_ctx);
ffmpeg_close_dst_ctx(ffmpeg_ctx); ffmpeg_close_dst_ctx(ffmpeg_ctx);
if(ffmpeg_ctx->io_ctx != NULL) {
av_free(ffmpeg_ctx->io_ctx->buffer);
av_free(ffmpeg_ctx->io_ctx);
lv_fs_close(&(ffmpeg_ctx->lv_file));
}
free(ffmpeg_ctx); free(ffmpeg_ctx);
LV_LOG_INFO("ffmpeg_ctx closed"); LV_LOG_INFO("ffmpeg_ctx closed");

View File

@ -59,7 +59,7 @@ int lv_ffmpeg_get_frame_num(const char * path);
lv_obj_t * lv_ffmpeg_player_create(lv_obj_t * parent); lv_obj_t * lv_ffmpeg_player_create(lv_obj_t * parent);
/** /**
* Set the path of the file to be played * Set the path of the file to be played.
* @param obj pointer to a ffmpeg_player object * @param obj pointer to a ffmpeg_player object
* @param path video file path * @param path video file path
* @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't get the info. * @return LV_RESULT_OK: no error; LV_RESULT_INVALID: can't get the info.

View File

@ -2994,6 +2994,16 @@
#define LV_FFMPEG_DUMP_FORMAT 0 #define LV_FFMPEG_DUMP_FORMAT 0
#endif #endif
#endif #endif
/** Use lvgl file path in FFmpeg Player widget
* You won't be able to open URLs after enabling this feature.
* Note that FFmpeg image decoder will always use lvgl file system. */
#ifndef LV_FFMPEG_PLAYER_USE_LV_FS
#ifdef CONFIG_LV_FFMPEG_PLAYER_USE_LV_FS
#define LV_FFMPEG_PLAYER_USE_LV_FS CONFIG_LV_FFMPEG_PLAYER_USE_LV_FS
#else
#define LV_FFMPEG_PLAYER_USE_LV_FS 0
#endif
#endif
#endif #endif
/*================== /*==================