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