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