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 
lv_ffmpeg_init(void)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 
lv_ffmpeg_get_frame_num(const char * path)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 
lv_ffmpeg_player_create(lv_obj_t * parent)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 
lv_ffmpeg_player_set_src(lv_obj_t * obj,const char * path)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 
lv_ffmpeg_player_set_cmd(lv_obj_t * obj,lv_ffmpeg_player_cmd_t cmd)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 
lv_ffmpeg_player_set_auto_restart(lv_obj_t * obj,bool en)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 
decoder_info(lv_img_decoder_t * decoder,const void * src,lv_img_header_t * header)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 
decoder_open(lv_img_decoder_t * decoder,lv_img_decoder_dsc_t * dsc)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 
decoder_close(lv_img_decoder_t * decoder,lv_img_decoder_dsc_t * dsc)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 
convert_color_depth(uint8_t * img,uint32_t px_cnt)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 
ffmpeg_get_img_data(struct ffmpeg_context_s * ffmpeg_ctx)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 
ffmpeg_pix_fmt_has_alpha(enum AVPixelFormat pix_fmt)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 
ffmpeg_pix_fmt_is_yuv(enum AVPixelFormat pix_fmt)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 
ffmpeg_output_video_frame(struct ffmpeg_context_s * ffmpeg_ctx)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 
ffmpeg_decode_packet(AVCodecContext * dec,const AVPacket * pkt,struct ffmpeg_context_s * ffmpeg_ctx)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 
ffmpeg_open_codec_context(int * stream_idx,AVCodecContext ** dec_ctx,AVFormatContext * fmt_ctx,enum AVMediaType type)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 
ffmpeg_get_img_header(const char * filepath,lv_img_header_t * header)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 
ffmpeg_get_frame_refr_period(struct ffmpeg_context_s * ffmpeg_ctx)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 
ffmpeg_update_next_frame(struct ffmpeg_context_s * ffmpeg_ctx)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 
ffmpeg_open_file(const char * path)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 
ffmpeg_image_allocate(struct ffmpeg_context_s * ffmpeg_ctx)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 
ffmpeg_close_src_ctx(struct ffmpeg_context_s * ffmpeg_ctx)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 
ffmpeg_close_dst_ctx(struct ffmpeg_context_s * ffmpeg_ctx)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 
ffmpeg_close(struct ffmpeg_context_s * ffmpeg_ctx)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 
lv_ffmpeg_player_frame_update_cb(lv_timer_t * timer)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 
lv_ffmpeg_player_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)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 
lv_ffmpeg_player_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)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*/
876