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