acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

lv_ffmpeg.c (25804B)

      1 /**
      2  * @file lv_ffmpeg.c
      3  *
      4  */
      5 
      6 /*********************
      7  *      INCLUDES
      8  *********************/
      9 #include "lv_ffmpeg.h"
     10 #if LV_USE_FFMPEG != 0
     11 
     12 #include <libavcodec/avcodec.h>
     13 #include <libavformat/avformat.h>
     14 #include <libavutil/imgutils.h>
     15 #include <libavutil/samplefmt.h>
     16 #include <libavutil/timestamp.h>
     17 #include <libswscale/swscale.h>
     18 
     19 /*********************
     20  *      DEFINES
     21  *********************/
     22 #if LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8
     23     #define AV_PIX_FMT_TRUE_COLOR AV_PIX_FMT_RGB8
     24 #elif LV_COLOR_DEPTH == 16
     25     #if LV_COLOR_16_SWAP == 0
     26         #define AV_PIX_FMT_TRUE_COLOR AV_PIX_FMT_RGB565LE
     27     #else
     28         #define AV_PIX_FMT_TRUE_COLOR AV_PIX_FMT_RGB565BE
     29     #endif
     30 #elif LV_COLOR_DEPTH == 32
     31     #define AV_PIX_FMT_TRUE_COLOR AV_PIX_FMT_BGR0
     32 #else
     33     #error Unsupported  LV_COLOR_DEPTH
     34 #endif
     35 
     36 #define MY_CLASS &lv_ffmpeg_player_class
     37 
     38 #define FRAME_DEF_REFR_PERIOD   33  /*[ms]*/
     39 
     40 /**********************
     41  *      TYPEDEFS
     42  **********************/
     43 struct ffmpeg_context_s {
     44     AVFormatContext * fmt_ctx;
     45     AVCodecContext * video_dec_ctx;
     46     AVStream * video_stream;
     47     uint8_t * video_src_data[4];
     48     uint8_t * video_dst_data[4];
     49     struct SwsContext * sws_ctx;
     50     AVFrame * frame;
     51     AVPacket pkt;
     52     int video_stream_idx;
     53     int video_src_linesize[4];
     54     int video_dst_linesize[4];
     55     enum AVPixelFormat video_dst_pix_fmt;
     56     bool has_alpha;
     57 };
     58 
     59 #pragma pack(1)
     60 
     61 struct lv_img_pixel_color_s {
     62     lv_color_t c;
     63     uint8_t alpha;
     64 };
     65 
     66 #pragma pack()
     67 
     68 /**********************
     69  *  STATIC PROTOTYPES
     70  **********************/
     71 
     72 static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header);
     73 static lv_res_t decoder_open(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc);
     74 static void decoder_close(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc);
     75 
     76 static struct ffmpeg_context_s * ffmpeg_open_file(const char * path);
     77 static void ffmpeg_close(struct ffmpeg_context_s * ffmpeg_ctx);
     78 static void ffmpeg_close_src_ctx(struct ffmpeg_context_s * ffmpeg_ctx);
     79 static void ffmpeg_close_dst_ctx(struct ffmpeg_context_s * ffmpeg_ctx);
     80 static int ffmpeg_image_allocate(struct ffmpeg_context_s * ffmpeg_ctx);
     81 static int ffmpeg_get_img_header(const char * path, lv_img_header_t * header);
     82 static int ffmpeg_get_frame_refr_period(struct ffmpeg_context_s * ffmpeg_ctx);
     83 static uint8_t * ffmpeg_get_img_data(struct ffmpeg_context_s * ffmpeg_ctx);
     84 static int ffmpeg_update_next_frame(struct ffmpeg_context_s * ffmpeg_ctx);
     85 static int ffmpeg_output_video_frame(struct ffmpeg_context_s * ffmpeg_ctx);
     86 static bool ffmpeg_pix_fmt_has_alpha(enum AVPixelFormat pix_fmt);
     87 static bool ffmpeg_pix_fmt_is_yuv(enum AVPixelFormat pix_fmt);
     88 
     89 static void lv_ffmpeg_player_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
     90 static void lv_ffmpeg_player_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
     91 
     92 #if LV_COLOR_DEPTH != 32
     93     static void convert_color_depth(uint8_t * img, uint32_t px_cnt);
     94 #endif
     95 
     96 /**********************
     97  *  STATIC VARIABLES
     98  **********************/
     99 const lv_obj_class_t lv_ffmpeg_player_class = {
    100     .constructor_cb = lv_ffmpeg_player_constructor,
    101     .destructor_cb = lv_ffmpeg_player_destructor,
    102     .instance_size = sizeof(lv_ffmpeg_player_t),
    103     .base_class = &lv_img_class
    104 };
    105 
    106 /**********************
    107  *      MACROS
    108  **********************/
    109 
    110 /**********************
    111  *   GLOBAL FUNCTIONS
    112  **********************/
    113 
    114 void lv_ffmpeg_init(void)
    115 {
    116     lv_img_decoder_t * dec = lv_img_decoder_create();
    117     lv_img_decoder_set_info_cb(dec, decoder_info);
    118     lv_img_decoder_set_open_cb(dec, decoder_open);
    119     lv_img_decoder_set_close_cb(dec, decoder_close);
    120 
    121 #if LV_FFMPEG_AV_DUMP_FORMAT == 0
    122     av_log_set_level(AV_LOG_QUIET);
    123 #endif
    124 }
    125 
    126 int lv_ffmpeg_get_frame_num(const char * path)
    127 {
    128     int ret = -1;
    129     struct ffmpeg_context_s * ffmpeg_ctx = ffmpeg_open_file(path);
    130 
    131     if(ffmpeg_ctx) {
    132         ret = ffmpeg_ctx->video_stream->nb_frames;
    133         ffmpeg_close(ffmpeg_ctx);
    134     }
    135 
    136     return ret;
    137 }
    138 
    139 lv_obj_t * lv_ffmpeg_player_create(lv_obj_t * parent)
    140 {
    141     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
    142     lv_obj_class_init_obj(obj);
    143     return obj;
    144 }
    145 
    146 lv_res_t lv_ffmpeg_player_set_src(lv_obj_t * obj, const char * path)
    147 {
    148     LV_ASSERT_OBJ(obj, MY_CLASS);
    149     lv_res_t res = LV_RES_INV;
    150 
    151     lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
    152 
    153     if(player->ffmpeg_ctx) {
    154         ffmpeg_close(player->ffmpeg_ctx);
    155         player->ffmpeg_ctx = NULL;
    156     }
    157 
    158     lv_timer_pause(player->timer);
    159 
    160     player->ffmpeg_ctx = ffmpeg_open_file(path);
    161 
    162     if(!player->ffmpeg_ctx) {
    163         LV_LOG_ERROR("ffmpeg file open failed: %s", path);
    164         goto failed;
    165     }
    166 
    167     if(ffmpeg_image_allocate(player->ffmpeg_ctx) < 0) {
    168         LV_LOG_ERROR("ffmpeg image allocate failed");
    169         ffmpeg_close(player->ffmpeg_ctx);
    170         goto failed;
    171     }
    172 
    173     bool has_alpha = player->ffmpeg_ctx->has_alpha;
    174     int width = player->ffmpeg_ctx->video_dec_ctx->width;
    175     int height = player->ffmpeg_ctx->video_dec_ctx->height;
    176     uint32_t data_size = 0;
    177 
    178     if(has_alpha) {
    179         data_size = width * height * LV_IMG_PX_SIZE_ALPHA_BYTE;
    180     }
    181     else {
    182         data_size = width * height * LV_COLOR_SIZE / 8;
    183     }
    184 
    185     player->imgdsc.header.always_zero = 0;
    186     player->imgdsc.header.w = width;
    187     player->imgdsc.header.h = height;
    188     player->imgdsc.data_size = data_size;
    189     player->imgdsc.header.cf = has_alpha ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR;
    190     player->imgdsc.data = ffmpeg_get_img_data(player->ffmpeg_ctx);
    191 
    192     lv_img_set_src(&player->img.obj, &(player->imgdsc));
    193 
    194     int period = ffmpeg_get_frame_refr_period(player->ffmpeg_ctx);
    195 
    196     if(period > 0) {
    197         LV_LOG_INFO("frame refresh period = %d ms, rate = %d fps",
    198                     period, 1000 / period);
    199         lv_timer_set_period(player->timer, period);
    200     }
    201     else {
    202         LV_LOG_WARN("unable to get frame refresh period");
    203     }
    204 
    205     res = LV_RES_OK;
    206 
    207 failed:
    208     return res;
    209 }
    210 
    211 void lv_ffmpeg_player_set_cmd(lv_obj_t * obj, lv_ffmpeg_player_cmd_t cmd)
    212 {
    213     LV_ASSERT_OBJ(obj, MY_CLASS);
    214     lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
    215 
    216     if(!player->ffmpeg_ctx) {
    217         LV_LOG_ERROR("ffmpeg_ctx is NULL");
    218         return;
    219     }
    220 
    221     lv_timer_t * timer = player->timer;
    222 
    223     switch(cmd) {
    224         case LV_FFMPEG_PLAYER_CMD_START:
    225             av_seek_frame(player->ffmpeg_ctx->fmt_ctx,
    226                           0, 0, AVSEEK_FLAG_BACKWARD);
    227             lv_timer_resume(timer);
    228             LV_LOG_INFO("ffmpeg player start");
    229             break;
    230         case LV_FFMPEG_PLAYER_CMD_STOP:
    231             av_seek_frame(player->ffmpeg_ctx->fmt_ctx,
    232                           0, 0, AVSEEK_FLAG_BACKWARD);
    233             lv_timer_pause(timer);
    234             LV_LOG_INFO("ffmpeg player stop");
    235             break;
    236         case LV_FFMPEG_PLAYER_CMD_PAUSE:
    237             lv_timer_pause(timer);
    238             LV_LOG_INFO("ffmpeg player pause");
    239             break;
    240         case LV_FFMPEG_PLAYER_CMD_RESUME:
    241             lv_timer_resume(timer);
    242             LV_LOG_INFO("ffmpeg player resume");
    243             break;
    244         default:
    245             LV_LOG_ERROR("Error cmd: %d", cmd);
    246             break;
    247     }
    248 }
    249 
    250 void lv_ffmpeg_player_set_auto_restart(lv_obj_t * obj, bool en)
    251 {
    252     LV_ASSERT_OBJ(obj, MY_CLASS);
    253     lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
    254     player->auto_restart = en;
    255 }
    256 
    257 /**********************
    258  *   STATIC FUNCTIONS
    259  **********************/
    260 
    261 static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header)
    262 {
    263     /* Get the source type */
    264     lv_img_src_t src_type = lv_img_src_get_type(src);
    265 
    266     if(src_type == LV_IMG_SRC_FILE) {
    267         const char * fn = src;
    268 
    269         if(ffmpeg_get_img_header(fn, header) < 0) {
    270             LV_LOG_ERROR("ffmpeg can't get image header");
    271             return LV_RES_INV;
    272         }
    273 
    274         return LV_RES_OK;
    275     }
    276 
    277     /* If didn't succeeded earlier then it's an error */
    278     return LV_RES_INV;
    279 }
    280 
    281 static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
    282 {
    283     if(dsc->src_type == LV_IMG_SRC_FILE) {
    284         const char * path = dsc->src;
    285 
    286         struct ffmpeg_context_s * ffmpeg_ctx = ffmpeg_open_file(path);
    287 
    288         if(ffmpeg_ctx == NULL) {
    289             return LV_RES_INV;
    290         }
    291 
    292         if(ffmpeg_image_allocate(ffmpeg_ctx) < 0) {
    293             LV_LOG_ERROR("ffmpeg image allocate failed");
    294             ffmpeg_close(ffmpeg_ctx);
    295             return LV_RES_INV;
    296         }
    297 
    298         if(ffmpeg_update_next_frame(ffmpeg_ctx) < 0) {
    299             ffmpeg_close(ffmpeg_ctx);
    300             LV_LOG_ERROR("ffmpeg update frame failed");
    301             return LV_RES_INV;
    302         }
    303 
    304         ffmpeg_close_src_ctx(ffmpeg_ctx);
    305         uint8_t * img_data = ffmpeg_get_img_data(ffmpeg_ctx);
    306 
    307 #if LV_COLOR_DEPTH != 32
    308         if(ffmpeg_ctx->has_alpha) {
    309             convert_color_depth(img_data, dsc->header.w * dsc->header.h);
    310         }
    311 #endif
    312 
    313         dsc->user_data = ffmpeg_ctx;
    314         dsc->img_data = img_data;
    315 
    316         /* The image is fully decoded. Return with its pointer */
    317         return LV_RES_OK;
    318     }
    319 
    320     /* If not returned earlier then it failed */
    321     return LV_RES_INV;
    322 }
    323 
    324 static void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc)
    325 {
    326     struct ffmpeg_context_s * ffmpeg_ctx = dsc->user_data;
    327     ffmpeg_close(ffmpeg_ctx);
    328 }
    329 
    330 #if LV_COLOR_DEPTH != 32
    331 
    332 static void convert_color_depth(uint8_t * img, uint32_t px_cnt)
    333 {
    334     lv_color32_t * img_src_p = (lv_color32_t *)img;
    335     struct lv_img_pixel_color_s * img_dst_p = (struct lv_img_pixel_color_s *)img;
    336 
    337     for(uint32_t i = 0; i < px_cnt; i++) {
    338         lv_color32_t temp = *img_src_p;
    339         img_dst_p->c = lv_color_hex(temp.full);
    340         img_dst_p->alpha = temp.ch.alpha;
    341 
    342         img_src_p++;
    343         img_dst_p++;
    344     }
    345 }
    346 
    347 #endif
    348 
    349 static uint8_t * ffmpeg_get_img_data(struct ffmpeg_context_s * ffmpeg_ctx)
    350 {
    351     uint8_t * img_data = ffmpeg_ctx->video_dst_data[0];
    352 
    353     if(img_data == NULL) {
    354         LV_LOG_ERROR("ffmpeg video dst data is NULL");
    355     }
    356 
    357     return img_data;
    358 }
    359 
    360 static bool ffmpeg_pix_fmt_has_alpha(enum AVPixelFormat pix_fmt)
    361 {
    362     const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get(pix_fmt);
    363 
    364     if(desc == NULL) {
    365         return false;
    366     }
    367 
    368     if(pix_fmt == AV_PIX_FMT_PAL8) {
    369         return true;
    370     }
    371 
    372     return (desc->flags & AV_PIX_FMT_FLAG_ALPHA) ? true : false;
    373 }
    374 
    375 static bool ffmpeg_pix_fmt_is_yuv(enum AVPixelFormat pix_fmt)
    376 {
    377     const AVPixFmtDescriptor * desc = av_pix_fmt_desc_get(pix_fmt);
    378 
    379     if(desc == NULL) {
    380         return false;
    381     }
    382 
    383     return !(desc->flags & AV_PIX_FMT_FLAG_RGB) && desc->nb_components >= 2;
    384 }
    385 
    386 static int ffmpeg_output_video_frame(struct ffmpeg_context_s * ffmpeg_ctx)
    387 {
    388     int ret = -1;
    389 
    390     int width = ffmpeg_ctx->video_dec_ctx->width;
    391     int height = ffmpeg_ctx->video_dec_ctx->height;
    392     AVFrame * frame = ffmpeg_ctx->frame;
    393 
    394     if(frame->width != width
    395        || frame->height != height
    396        || frame->format != ffmpeg_ctx->video_dec_ctx->pix_fmt) {
    397 
    398         /* To handle this change, one could call av_image_alloc again and
    399          * decode the following frames into another rawvideo file.
    400          */
    401         LV_LOG_ERROR("Width, height and pixel format have to be "
    402                      "constant in a rawvideo file, but the width, height or "
    403                      "pixel format of the input video changed:\n"
    404                      "old: width = %d, height = %d, format = %s\n"
    405                      "new: width = %d, height = %d, format = %s\n",
    406                      width,
    407                      height,
    408                      av_get_pix_fmt_name(ffmpeg_ctx->video_dec_ctx->pix_fmt),
    409                      frame->width, frame->height,
    410                      av_get_pix_fmt_name(frame->format));
    411         goto failed;
    412     }
    413 
    414     LV_LOG_TRACE("video_frame coded_n:%d", frame->coded_picture_number);
    415 
    416     /* copy decoded frame to destination buffer:
    417      * this is required since rawvideo expects non aligned data
    418      */
    419     av_image_copy(ffmpeg_ctx->video_src_data, ffmpeg_ctx->video_src_linesize,
    420                   (const uint8_t **)(frame->data), frame->linesize,
    421                   ffmpeg_ctx->video_dec_ctx->pix_fmt, width, height);
    422 
    423     if(ffmpeg_ctx->sws_ctx == NULL) {
    424         int swsFlags = SWS_BILINEAR;
    425 
    426         if(ffmpeg_pix_fmt_is_yuv(ffmpeg_ctx->video_dec_ctx->pix_fmt)) {
    427 
    428             /* When the video width and height are not multiples of 8,
    429              * and there is no size change in the conversion,
    430              * a blurry screen will appear on the right side
    431              * This problem was discovered in 2012 and
    432              * continues to exist in version 4.1.3 in 2019
    433              * This problem can be avoided by increasing SWS_ACCURATE_RND
    434              */
    435             if((width & 0x7) || (height & 0x7)) {
    436                 LV_LOG_WARN("The width(%d) and height(%d) the image "
    437                             "is not a multiple of 8, "
    438                             "the decoding speed may be reduced",
    439                             width, height);
    440                 swsFlags |= SWS_ACCURATE_RND;
    441             }
    442         }
    443 
    444         ffmpeg_ctx->sws_ctx = sws_getContext(
    445                                   width, height, ffmpeg_ctx->video_dec_ctx->pix_fmt,
    446                                   width, height, ffmpeg_ctx->video_dst_pix_fmt,
    447                                   swsFlags,
    448                                   NULL, NULL, NULL);
    449     }
    450 
    451     if(!ffmpeg_ctx->has_alpha) {
    452         int lv_linesize = sizeof(lv_color_t) * width;
    453         int dst_linesize = ffmpeg_ctx->video_dst_linesize[0];
    454         if(dst_linesize != lv_linesize) {
    455             LV_LOG_WARN("ffmpeg linesize = %d, but lvgl image require %d",
    456                         dst_linesize,
    457                         lv_linesize);
    458             ffmpeg_ctx->video_dst_linesize[0] = lv_linesize;
    459         }
    460     }
    461 
    462     ret = sws_scale(
    463               ffmpeg_ctx->sws_ctx,
    464               (const uint8_t * const *)(ffmpeg_ctx->video_src_data),
    465               ffmpeg_ctx->video_src_linesize,
    466               0,
    467               height,
    468               ffmpeg_ctx->video_dst_data,
    469               ffmpeg_ctx->video_dst_linesize);
    470 
    471 failed:
    472     return ret;
    473 }
    474 
    475 static int ffmpeg_decode_packet(AVCodecContext * dec, const AVPacket * pkt,
    476                                 struct ffmpeg_context_s * ffmpeg_ctx)
    477 {
    478     int ret = 0;
    479 
    480     /* submit the packet to the decoder */
    481     ret = avcodec_send_packet(dec, pkt);
    482     if(ret < 0) {
    483         LV_LOG_ERROR("Error submitting a packet for decoding (%s)",
    484                      av_err2str(ret));
    485         return ret;
    486     }
    487 
    488     /* get all the available frames from the decoder */
    489     while(ret >= 0) {
    490         ret = avcodec_receive_frame(dec, ffmpeg_ctx->frame);
    491         if(ret < 0) {
    492 
    493             /* those two return values are special and mean there is
    494              * no output frame available,
    495              * but there were no errors during decoding
    496              */
    497             if(ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) {
    498                 return 0;
    499             }
    500 
    501             LV_LOG_ERROR("Error during decoding (%s)", av_err2str(ret));
    502             return ret;
    503         }
    504 
    505         /* write the frame data to output file */
    506         if(dec->codec->type == AVMEDIA_TYPE_VIDEO) {
    507             ret = ffmpeg_output_video_frame(ffmpeg_ctx);
    508         }
    509 
    510         av_frame_unref(ffmpeg_ctx->frame);
    511         if(ret < 0) {
    512             LV_LOG_WARN("ffmpeg_decode_packet ended %d", ret);
    513             return ret;
    514         }
    515     }
    516 
    517     return 0;
    518 }
    519 
    520 static int ffmpeg_open_codec_context(int * stream_idx,
    521                                      AVCodecContext ** dec_ctx, AVFormatContext * fmt_ctx,
    522                                      enum AVMediaType type)
    523 {
    524     int ret;
    525     int stream_index;
    526     AVStream * st;
    527     AVCodec * dec = NULL;
    528     AVDictionary * opts = NULL;
    529 
    530     ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0);
    531     if(ret < 0) {
    532         LV_LOG_ERROR("Could not find %s stream in input file",
    533                      av_get_media_type_string(type));
    534         return ret;
    535     }
    536     else {
    537         stream_index = ret;
    538         st = fmt_ctx->streams[stream_index];
    539 
    540         /* find decoder for the stream */
    541         dec = avcodec_find_decoder(st->codecpar->codec_id);
    542         if(dec == NULL) {
    543             LV_LOG_ERROR("Failed to find %s codec",
    544                          av_get_media_type_string(type));
    545             return AVERROR(EINVAL);
    546         }
    547 
    548         /* Allocate a codec context for the decoder */
    549         *dec_ctx = avcodec_alloc_context3(dec);
    550         if(*dec_ctx == NULL) {
    551             LV_LOG_ERROR("Failed to allocate the %s codec context",
    552                          av_get_media_type_string(type));
    553             return AVERROR(ENOMEM);
    554         }
    555 
    556         /* Copy codec parameters from input stream to output codec context */
    557         if((ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0) {
    558             LV_LOG_ERROR(
    559                 "Failed to copy %s codec parameters to decoder context",
    560                 av_get_media_type_string(type));
    561             return ret;
    562         }
    563 
    564         /* Init the decoders */
    565         if((ret = avcodec_open2(*dec_ctx, dec, &opts)) < 0) {
    566             LV_LOG_ERROR("Failed to open %s codec",
    567                          av_get_media_type_string(type));
    568             return ret;
    569         }
    570 
    571         *stream_idx = stream_index;
    572     }
    573 
    574     return 0;
    575 }
    576 
    577 static int ffmpeg_get_img_header(const char * filepath,
    578                                  lv_img_header_t * header)
    579 {
    580     int ret = -1;
    581 
    582     AVFormatContext * fmt_ctx = NULL;
    583     AVCodecContext * video_dec_ctx = NULL;
    584     int video_stream_idx;
    585 
    586     /* open input file, and allocate format context */
    587     if(avformat_open_input(&fmt_ctx, filepath, NULL, NULL) < 0) {
    588         LV_LOG_ERROR("Could not open source file %s", filepath);
    589         goto failed;
    590     }
    591 
    592     /* retrieve stream information */
    593     if(avformat_find_stream_info(fmt_ctx, NULL) < 0) {
    594         LV_LOG_ERROR("Could not find stream information");
    595         goto failed;
    596     }
    597 
    598     if(ffmpeg_open_codec_context(&video_stream_idx, &video_dec_ctx,
    599                                  fmt_ctx, AVMEDIA_TYPE_VIDEO)
    600        >= 0) {
    601         bool has_alpha = ffmpeg_pix_fmt_has_alpha(video_dec_ctx->pix_fmt);
    602 
    603         /* allocate image where the decoded image will be put */
    604         header->w = video_dec_ctx->width;
    605         header->h = video_dec_ctx->height;
    606         header->always_zero = 0;
    607         header->cf = (has_alpha ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR);
    608 
    609         ret = 0;
    610     }
    611 
    612 failed:
    613     avcodec_free_context(&video_dec_ctx);
    614     avformat_close_input(&fmt_ctx);
    615 
    616     return ret;
    617 }
    618 
    619 static int ffmpeg_get_frame_refr_period(struct ffmpeg_context_s * ffmpeg_ctx)
    620 {
    621     int avg_frame_rate_num = ffmpeg_ctx->video_stream->avg_frame_rate.num;
    622     if(avg_frame_rate_num > 0) {
    623         int period = 1000 * (int64_t)ffmpeg_ctx->video_stream->avg_frame_rate.den
    624                      / avg_frame_rate_num;
    625         return period;
    626     }
    627 
    628     return -1;
    629 }
    630 
    631 static int ffmpeg_update_next_frame(struct ffmpeg_context_s * ffmpeg_ctx)
    632 {
    633     int ret = 0;
    634 
    635     while(1) {
    636 
    637         /* read frames from the file */
    638         if(av_read_frame(ffmpeg_ctx->fmt_ctx, &(ffmpeg_ctx->pkt)) >= 0) {
    639             bool is_image = false;
    640 
    641             /* check if the packet belongs to a stream we are interested in,
    642              * otherwise skip it
    643              */
    644             if(ffmpeg_ctx->pkt.stream_index == ffmpeg_ctx->video_stream_idx) {
    645                 ret = ffmpeg_decode_packet(ffmpeg_ctx->video_dec_ctx,
    646                                            &(ffmpeg_ctx->pkt), ffmpeg_ctx);
    647                 is_image = true;
    648             }
    649 
    650             av_packet_unref(&(ffmpeg_ctx->pkt));
    651 
    652             if(ret < 0) {
    653                 LV_LOG_WARN("video frame is empty %d", ret);
    654                 break;
    655             }
    656 
    657             /* Used to filter data that is not an image */
    658             if(is_image) {
    659                 break;
    660             }
    661         }
    662         else {
    663             ret = -1;
    664             break;
    665         }
    666     }
    667 
    668     return ret;
    669 }
    670 
    671 struct ffmpeg_context_s * ffmpeg_open_file(const char * path)
    672 {
    673     if(path == NULL || strlen(path) == 0) {
    674         LV_LOG_ERROR("file path is empty");
    675         return NULL;
    676     }
    677 
    678     struct ffmpeg_context_s * ffmpeg_ctx = calloc(1, sizeof(struct ffmpeg_context_s));
    679 
    680     if(ffmpeg_ctx == NULL) {
    681         LV_LOG_ERROR("ffmpeg_ctx malloc failed");
    682         goto failed;
    683     }
    684 
    685     /* open input file, and allocate format context */
    686 
    687     if(avformat_open_input(&(ffmpeg_ctx->fmt_ctx), path, NULL, NULL) < 0) {
    688         LV_LOG_ERROR("Could not open source file %s", path);
    689         goto failed;
    690     }
    691 
    692     /* retrieve stream information */
    693 
    694     if(avformat_find_stream_info(ffmpeg_ctx->fmt_ctx, NULL) < 0) {
    695         LV_LOG_ERROR("Could not find stream information");
    696         goto failed;
    697     }
    698 
    699     if(ffmpeg_open_codec_context(
    700            &(ffmpeg_ctx->video_stream_idx),
    701            &(ffmpeg_ctx->video_dec_ctx),
    702            ffmpeg_ctx->fmt_ctx, AVMEDIA_TYPE_VIDEO)
    703        >= 0) {
    704         ffmpeg_ctx->video_stream = ffmpeg_ctx->fmt_ctx->streams[ffmpeg_ctx->video_stream_idx];
    705 
    706         ffmpeg_ctx->has_alpha = ffmpeg_pix_fmt_has_alpha(ffmpeg_ctx->video_dec_ctx->pix_fmt);
    707 
    708         ffmpeg_ctx->video_dst_pix_fmt = (ffmpeg_ctx->has_alpha ? AV_PIX_FMT_BGRA : AV_PIX_FMT_TRUE_COLOR);
    709     }
    710 
    711 #if LV_FFMPEG_AV_DUMP_FORMAT != 0
    712     /* dump input information to stderr */
    713     av_dump_format(ffmpeg_ctx->fmt_ctx, 0, path, 0);
    714 #endif
    715 
    716     if(ffmpeg_ctx->video_stream == NULL) {
    717         LV_LOG_ERROR("Could not find video stream in the input, aborting");
    718         goto failed;
    719     }
    720 
    721     return ffmpeg_ctx;
    722 
    723 failed:
    724     ffmpeg_close(ffmpeg_ctx);
    725     return NULL;
    726 }
    727 
    728 static int ffmpeg_image_allocate(struct ffmpeg_context_s * ffmpeg_ctx)
    729 {
    730     int ret;
    731 
    732     /* allocate image where the decoded image will be put */
    733     ret = av_image_alloc(
    734               ffmpeg_ctx->video_src_data,
    735               ffmpeg_ctx->video_src_linesize,
    736               ffmpeg_ctx->video_dec_ctx->width,
    737               ffmpeg_ctx->video_dec_ctx->height,
    738               ffmpeg_ctx->video_dec_ctx->pix_fmt,
    739               4);
    740 
    741     if(ret < 0) {
    742         LV_LOG_ERROR("Could not allocate src raw video buffer");
    743         return ret;
    744     }
    745 
    746     LV_LOG_INFO("alloc video_src_bufsize = %d", ret);
    747 
    748     ret = av_image_alloc(
    749               ffmpeg_ctx->video_dst_data,
    750               ffmpeg_ctx->video_dst_linesize,
    751               ffmpeg_ctx->video_dec_ctx->width,
    752               ffmpeg_ctx->video_dec_ctx->height,
    753               ffmpeg_ctx->video_dst_pix_fmt,
    754               4);
    755 
    756     if(ret < 0) {
    757         LV_LOG_ERROR("Could not allocate dst raw video buffer");
    758         return ret;
    759     }
    760 
    761     LV_LOG_INFO("allocate video_dst_bufsize = %d", ret);
    762 
    763     ffmpeg_ctx->frame = av_frame_alloc();
    764 
    765     if(ffmpeg_ctx->frame == NULL) {
    766         LV_LOG_ERROR("Could not allocate frame");
    767         return -1;
    768     }
    769 
    770     /* initialize packet, set data to NULL, let the demuxer fill it */
    771     av_init_packet(&ffmpeg_ctx->pkt);
    772     ffmpeg_ctx->pkt.data = NULL;
    773     ffmpeg_ctx->pkt.size = 0;
    774 
    775     return 0;
    776 }
    777 
    778 static void ffmpeg_close_src_ctx(struct ffmpeg_context_s * ffmpeg_ctx)
    779 {
    780     avcodec_free_context(&(ffmpeg_ctx->video_dec_ctx));
    781     avformat_close_input(&(ffmpeg_ctx->fmt_ctx));
    782     av_frame_free(&(ffmpeg_ctx->frame));
    783     if(ffmpeg_ctx->video_src_data[0] != NULL) {
    784         av_free(ffmpeg_ctx->video_src_data[0]);
    785         ffmpeg_ctx->video_src_data[0] = NULL;
    786     }
    787 }
    788 
    789 static void ffmpeg_close_dst_ctx(struct ffmpeg_context_s * ffmpeg_ctx)
    790 {
    791     if(ffmpeg_ctx->video_dst_data[0] != NULL) {
    792         av_free(ffmpeg_ctx->video_dst_data[0]);
    793         ffmpeg_ctx->video_dst_data[0] = NULL;
    794     }
    795 }
    796 
    797 static void ffmpeg_close(struct ffmpeg_context_s * ffmpeg_ctx)
    798 {
    799     if(ffmpeg_ctx == NULL) {
    800         LV_LOG_WARN("ffmpeg_ctx is NULL");
    801         return;
    802     }
    803 
    804     sws_freeContext(ffmpeg_ctx->sws_ctx);
    805     ffmpeg_close_src_ctx(ffmpeg_ctx);
    806     ffmpeg_close_dst_ctx(ffmpeg_ctx);
    807     free(ffmpeg_ctx);
    808 
    809     LV_LOG_INFO("ffmpeg_ctx closed");
    810 }
    811 
    812 static void lv_ffmpeg_player_frame_update_cb(lv_timer_t * timer)
    813 {
    814     lv_obj_t * obj = (lv_obj_t *)timer->user_data;
    815     lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
    816 
    817     if(!player->ffmpeg_ctx) {
    818         return;
    819     }
    820 
    821     int has_next = ffmpeg_update_next_frame(player->ffmpeg_ctx);
    822 
    823     if(has_next < 0) {
    824         lv_ffmpeg_player_set_cmd(obj, player->auto_restart ? LV_FFMPEG_PLAYER_CMD_START : LV_FFMPEG_PLAYER_CMD_STOP);
    825         return;
    826     }
    827 
    828 #if LV_COLOR_DEPTH != 32
    829     if(player->ffmpeg_ctx->has_alpha) {
    830         convert_color_depth((uint8_t *)(player->imgdsc.data),
    831                             player->imgdsc.header.w * player->imgdsc.header.h);
    832     }
    833 #endif
    834 
    835     lv_img_cache_invalidate_src(lv_img_get_src(obj));
    836     lv_obj_invalidate(obj);
    837 }
    838 
    839 static void lv_ffmpeg_player_constructor(const lv_obj_class_t * class_p,
    840                                          lv_obj_t * obj)
    841 {
    842     LV_TRACE_OBJ_CREATE("begin");
    843 
    844     lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
    845 
    846     player->auto_restart = false;
    847     player->ffmpeg_ctx = NULL;
    848     player->timer = lv_timer_create(lv_ffmpeg_player_frame_update_cb,
    849                                     FRAME_DEF_REFR_PERIOD, obj);
    850     lv_timer_pause(player->timer);
    851 
    852     LV_TRACE_OBJ_CREATE("finished");
    853 }
    854 
    855 static void lv_ffmpeg_player_destructor(const lv_obj_class_t * class_p,
    856                                         lv_obj_t * obj)
    857 {
    858     LV_TRACE_OBJ_CREATE("begin");
    859 
    860     lv_ffmpeg_player_t * player = (lv_ffmpeg_player_t *)obj;
    861 
    862     if(player->timer) {
    863         lv_timer_del(player->timer);
    864         player->timer = NULL;
    865     }
    866 
    867     lv_img_cache_invalidate_src(lv_img_get_src(obj));
    868 
    869     ffmpeg_close(player->ffmpeg_ctx);
    870     player->ffmpeg_ctx = NULL;
    871 
    872     LV_TRACE_OBJ_CREATE("finished");
    873 }
    874 
    875 #endif /*LV_USE_FFMPEG*/