mirror of
https://github.com/lvgl/lvgl.git
synced 2024-11-23 01:33:59 +08:00
fix(ffmpeg): add native filesystem API support for FFmpeg image decoder (#7253)
This commit is contained in:
parent
11be75e302
commit
4a77a05fb3
7
Kconfig
7
Kconfig
@ -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"
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
|
||||||
/*==================
|
/*==================
|
||||||
|
@ -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");
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
|
||||||
/*==================
|
/*==================
|
||||||
|
Loading…
Reference in New Issue
Block a user