Navigation

    全志在线开发者论坛

    • Register
    • Login
    • Search
    • Categories
    • Tags
    • 在线文档
    • 社区主页

    动手让LVGL8带的Music-Demo音乐响起来(代码已上传)

    GUI
    12
    20
    10397
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • X
      xfdr0805 LV 5 last edited by whycan

      1. LVGL8 自带的有一个音乐播放器的Demo,效果不错,充分展示了LVGL8的弹性网格布局,动画等效果,来学习GUI是非常不错的,只是这个Demo并不能真正的播放音乐,但是已经把音乐播放的逻辑都给实现了,现在我们就动手给它注入灵魂----mpv
      2. 最初是用的sox这个号称音频界的瑞士军刀的工具,但是编写过程中碰到一个比较棘手的问题,那就是无法使用管道重定向输出(play本身不支持管道,只能借助sox),也就没办法获取播放进度,会造成进度条时间与实际播放时间有点误差,最后就放弃使用sox了

      2021-09-19_11-14.png

              if (pid == 0)
              {
                  LOG_D("child pid:%d\n", getpid());
                  char cmd[32];
                  prctl(PR_SET_PDEATHSIG, SIGKILL);
                  close(0);
                  dup2(pip[1], 1); //标准输出重定向到管道输出
                  close(pip[0]);
                  sprintf(buf, "./music/%s", _lv_demo_music_get_title(track_id));
                  sprintf(cmd, "--start=%d", _time, _lv_demo_music_get_track_length(track_id));
                  // execlp("ls", "ls", "./music", NULL);
                  // execlp("play", "play", "-p", buf, "trim", cmd, NULL);
                  // execlp("sox", "sox", buf, "-p", "|", "play", "-", "trim", cmd, NULL);
                  //最后知道怎么使用管道了,但是这是2个进程
                  // sox ./music/云非非\ -\ 邂逅.flac -t flac - | play -t flac - &
                  // sox ./music/云非非\ -\ 邂逅.flac -t flac - | play - &
                  // sox ./music/云非非\ -\ 邂逅.flac -p | play - &
                  return 0;
              } 
      
      1. 下边请出在后台默默工作的大佬MPV

        MPV播放器是什么?

        MPV是著名开源播放器mplayer和mplayer2的一个分支。
        mplayer则是这个地球上最强的播放器(没有之一),跨平台的特性使得windows、mac、linux上都可以见到它的身影,电脑、手机上很多播放器也是基于它开发的,由于mplayer不带界面,所以很多时候你都不知道是它在默默为你工作。
        并且mplayer播放视频时对于资源的消耗往往最少,所以你会发现在一台配置极差的电脑上播放高清电影,mplayer通常是最流畅的,使用快进时最能体现出差距,其他播放器已经画面卡死时,mplayer的画面可能只是感觉到掉帧而已。
        MPV播放器继承这些众多优良特性的同时,添加了内置于窗口的播放控制界面(OSC),对硬解的良好支持,以及其他额外特性。由于口碑很好,使得著名的mplayer前端:smplayer在不久前也添加了对MPV的支持,现在的smplayer你可以在mplayer和MPV2个核心之间切换使用。
        mpv官网的开发文档比较好,参考使用手册都可以使用起来

      2. 下边简单说一下过程
        a.首先要获取播放列表 播放列表里的字体是浪漫雅圆,使用freetype渲染的 。获取列表有2种方法
          方法1:
        
          int scan_music_list(char *_path)
          {
              char path[128];
              sprintf(path, "ls %s", _path);
              FILE *fp = popen(path, "r");
              if (ferror(fp))
              {
                  LOG_D("error\n");
              }
              char buf[128];
              while (!feof(fp))
              {
                  fgets(buf, sizeof(buf), fp);
                  strncpy(title_list[idx], buf, strlen(buf) - 1); //去掉文件名后的\n
                  get_music_info(title_list[idx], idx);
                  idx++;
                  usleep(1);
              }
              return idx;
          } 
          
        
         方法2:
        
         int list_dir(char *path, int depth)
         {
             DIR *dir;
             struct dirent *file;
             struct stat st;
             dir = opendir(path);
             if (!dir)
             {
                 LOG_D("open dir %s failed!", path);
                 return -1;
             }
             LOG_D("open dir %s ok!", path);
             while ((file = readdir(dir)) != NULL)
             {
                 if (strncmp(file->d_name, ".", 1) == 0 || strncmp(file->d_name, "..", 2) == 0)
                 {
                     continue;
                 }
                 strcpy(title_list[idx++], file->d_name);
                 if (stat(file->d_name, &st) >= 0 && S_ISDIR(st.st_mode) && depth <= 5)
                 {
                     list_dir(file->d_name, depth + 1);
                 }
             }
             closedir(dir);
             return 0;
         }      ```
        
        b.程序启动时创建一个子进程,一个线程获取播放进度,一个线程获取各种输入
          void lv_demo_music(void)
          {
              lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x343247), 0);
        
              music_num = scan_music_list(MUSIC_PATH);
              list = _lv_demo_music_list_create(lv_scr_act());
              ctrl = _lv_demo_music_main_create(lv_scr_act());
              pid = vfork();
              if (pid == 0)
              {
                  LOG_D("child pid:%d\n", getpid());
                  prctl(PR_SET_PDEATHSIG, SIGKILL);
                  execlp("mpv", "mpv", "--quiet", "--no-terminal", "--no-video", "--idle=yes", "--term-status-msg=", "--input-ipc-server=/tmp/mpvsocket", NULL);
                  LOG_D("child exit!\n");
                  return 0;
              }
              else if (pid > 0)
              {
                  LOG_D("parent pid:%d\n", getpid());
                  sleep(1);
                  close(0);
                  act.sa_handler = sigusr1;
                  sigfillset(&act.sa_mask);
                  act.sa_flags = SA_RESTART; /* don't fiddle with EINTR */
                  sigaction(SIGUSR1, &act, NULL);
                  addr.sun_family = AF_UNIX;
                  strcpy(addr.sun_path, "/tmp/mpvsocket");
                  fd_mpv = socket(AF_UNIX, SOCK_STREAM, 0);
                  if (fd_mpv == -1)
                  {
                      LOG_D("Create socket failed\n");
                  }
                  if (connect(fd_mpv, (struct sockaddr *)&addr, sizeof(addr)) == -1)
                  {
                      LOG_D("Cannot connect to socket %s\n", addr.sun_path);
                  }
                  if (pthread_create(&mthread, NULL, get_music_percent_pos, NULL) != 0)
                  {
                      LOG_D("pthread create error!\n");
                      return 0;
                  }
                  // LOG_D("get music pos pthread create ok!\n");
              }
              else
              {
                  LOG_D("fork error:\n");
              }
        
          #if LV_DEMO_MUSIC_AUTO_PLAY
              lv_timer_create(auto_step_cb, 1000, NULL);
          #endif
          }
        
          这里说明一下mpv里的参数
          "--quiet", //输出尽量少的信息
          "--no-terminal", //不接受终端输入
          "--no-video", //不需要视频
          "--idle=yes", //播放完不能出进程
          "--term-status-msg=", //状态信息不打印
          "--input-ipc-server=/tmp/mpvsocket"//使用sock方式与mpv通信
          这里使用的是mpv推荐的基于socket的JSON-based IPC protocol通信方式
        
        c. 一个线程启动时向mpv发送命令,然后监听事件就可以知道当前进度及状态
          void *get_music_percent_pos(void *arg)
          {
              // char cmd[] = "{\"command\": [ \"get_property\", \"playback-time\"] }\n";
              // char cmd[] = "{\"command\": [ \"get_property\", \"percent-pos\"] ,\"request_id\":2}\n";
              char cmd2[] = "{\"command\": [ \"observe_property\",2, \"percent-pos\"]}\n";
              char cmd1[] = "{\"command\": [  \"observe_property\",1,\"time-pos\"]}\n";
              char buf[512];
              cJSON *root;
              cJSON *event;
              cJSON *percent;
              write(fd_mpv, cmd1, strlen(cmd1));
              _msleep(100);
              write(fd_mpv, cmd2, strlen(cmd2));
              memset(buf, 0, sizeof(buf));
              while (1)
              {
        
                  if (read(fd_mpv, buf, sizeof(buf)) > 0)
                  {
                      //LOG_D("--->:%s\n", buf);
                      root = cJSON_Parse(buf);
                      if (root == NULL)
                      {
                          LOG_D("cJSON parse error!\n");
                          return;
                      }
                      // LOG_D("cJSON parse ok!\n");
                      if (cJSON_HasObjectItem(root, "event"))
                      {
        
                          event = cJSON_GetObjectItem(root, "event");
                          if (event != NULL)
                          {
                              if (strcmp(event->valuestring, "start-file") == 0)
                              {
                              }
                              else if (strcmp(event->valuestring, "metadata-update") == 0)
                              {
                              }
                              else if (strcmp(event->valuestring, "file-loaded") == 0)
                              {
                              }
                              else if (strcmp(event->valuestring, "property-change") == 0 && (cJSON_GetObjectItem(root, "id")->valueint == 1))
                              {
                                  percent = cJSON_GetObjectItem(root, "data");
                                  lv_label_set_text_fmt(time_obj, "%02d:%02d", (int)percent->valuedouble / 60, (int)percent->valuedouble % 60);
                                  LOG_D("time pos:%d\n", (int)percent->valuedouble);
                              }
                              else if (strcmp(event->valuestring, "property-change") == 0 && (cJSON_GetObjectItem(root, "id")->valueint == 2))
                              {
                                  percent = cJSON_GetObjectItem(root, "data");
                                  lv_slider_set_value(slider_obj, (int)percent->valuedouble, LV_ANIM_ON);
                                  LOG_D("percent pos:%d\n", (int)percent->valuedouble);
                              }
                              else if (strcmp(event->valuestring, "end-file") == 0)
                              {
                                  LOG_D("end-file\n");
                                  is_loaded = true;
                                  _lv_demo_music_album_next(true);
                              }
                              else if (strcmp(event->valuestring, "playback-restart") == 0)
                              {
                              }
                              else if (strcmp(event->valuestring, "idle") == 0)
                              {
                                  LOG_D("idle\n");
                              }
                          }
                      }
        
                      free(root);
                  }
                  //_msleep(250);
              }
              pthread_exit(NULL);
              LOG_D("pthread exit:\n");
          }
          这里通信都是采用JSON格式,使用了cJSON来解析送来的数据
              ``````
        
      3. 这些实现完成后,音乐播放器也就有了灵魂,就是一个真正的音乐播放器了,几乎支持所有音乐格式,我测试的常见的有wav,mp3,flac,ape,dts等
      4. 效果预览 (240*320) 项目位置:D1扩展板
      1 Reply Last reply Reply Quote Share 9
      • aldfaaa
        whycan矿工-小叶 LV 7 last edited by

        flac的音质也太棒了吧

        1 Reply Last reply Reply Quote Share 1
        • B
          btgod001 LV 2 last edited by

          这是把MPV移植到D1 tina了吗? 有移植包吗?

          S 1 Reply Last reply Reply Quote Share 0
          • tigger
            tigger LV 7 last edited by

            请问楼主有完整工程吗?

            1 Reply Last reply Reply Quote Share 0
            • whycan
              whycan晕哥 LV 9 last edited by

              4695f4b3-6640-4368-a705-03aa2843128a-4308ccfc1ac68068d09380581e5e5cf.jpg

              感谢楼主 @xfdr0805 ,已经拿到源码,GUI没有问题了,但是ubuntu16.04 mpv 创建 socket 文件失败,正在检查原因。

              M 1 Reply Last reply Reply Quote Share 0
              • M
                mhcsoft LV 5 @whycan last edited by

                @whycan 晕哥求源码

                whycan 1 Reply Last reply Reply Quote Share 0
                • whycan
                  whycan晕哥 LV 9 @mhcsoft last edited by

                  @mhcsoft

                  @xfdr0805 楼主的源码: music-player.7z

                  我测试发现ubuntu18.04 还是有点小坑,按键不听使唤,哪位大佬继续修复一下。

                  1 Reply Last reply Reply Quote Share 0
                  • whycan
                    whycan晕哥 LV 9 last edited by whycan

                    捋了一下,楼主用进程通讯的,先启动后台进程:

                    mpv --quiet --no-terminal --no-video --idle=yes --term-status-msg= --input-ipc-server=/tmp/mpvsocket
                    

                    然后前台使用命令通讯

                    装载文件:

                    echo '{ "command": ["loadfile", "/mnt/hgfs/D/music/3.wav"] }' | socat - /tmp/mpvsocket
                    

                    暂停:

                    echo '{ "command": ["set_property", "pause", true] }' | socat - /tmp/mpvsocket
                    

                    继续:

                    echo '{ "command": ["set_property", "pause", false] }' | socat - /tmp/mpvsocket
                    

                    播放进度:

                    echo '{ "command": ["get_property", "playback-time"] }' | socat - /tmp/mpvsocket
                    

                    获取当前播放文件名:

                    echo '{ "command": ["get_property", "filename"] }' | socat - /tmp/mpvsocket
                    

                    获取总时长:

                    echo '{ "command": ["get_property", "duration"] }' | socat - /tmp/mpvsocket
                    

                    剩余时长:

                    echo '{ "command": ["get_property", "time-remaining"] }' | socat - /tmp/mpvsocket
                    

                    播放进度:

                    echo '{ "command": ["get_property", "time-pos"] }' | socat - /tmp/mpvsocket
                    

                    文件信息:

                    echo '{ "command": ["get_property", "filtered-metadata"] }' | socat - /tmp/mpvsocket
                    

                    设置音量:

                    echo '{ "command": ["set_property", "volume", 80] }' | socat - /tmp/mpvsocket
                    

                    获取当前音量:

                    echo '{ "command": ["get_property", "volume"] }' | socat - /tmp/mpvsocket
                    

                    绝对定位(180秒):

                    echo '{ "command": ["seek", 180, "absolute"] }' | socat - /tmp/mpvsocket
                    

                    相对定位(往前180秒):

                    echo '{ "command": ["seek", 180, "relative"] }' | socat - /tmp/mpvsocket
                    

                    添加到播放列表:

                    echo '{ "command": ["loadfile", "/mnt/hgfs/D/music/08 初恋女.wav", "append"] }' | socat - /tmp/mpvsocket
                    

                    显示播放列表:

                    echo '{ "command": ["get_property", "playlist"] }' | socat - /tmp/mpvsocket
                    

                    显示当前播放的音乐文件名:

                    echo '{ "command": ["get_property", "media-title"] }' | socat - /tmp/mpvsocket
                    

                    显示播放列表音乐数量:

                    echo '{ "command": ["get_property", "playlist-count"] }' | socat - /tmp/mpvsocket
                    

                    播放列表序号是2的歌曲:

                    echo '{ "command": ["set_property", "playlist-pos", 2] }' | socat - /tmp/mpvsocket
                    

                    获取当前播放的歌曲在列表中的序号:

                    echo '{ "command": ["get_property", "playlist-pos"] }' | socat - /tmp/mpvsocket
                    

                    获取工作目录:

                    echo '{ "command": ["get_property", "working-directory"] }' | socat - /tmp/mpvsocket
                    

                    获取播放列表序号3的文件名:

                    echo '{ "command": ["get_property", "playlist/3/filename"] }' | socat - /tmp/mpvsocket
                    

                    设置循环播放:

                    echo '{ "command": ["set_property", "loop", true] }' | socat - /tmp/mpvsocket
                    

                    读取循环播放设置:

                    echo '{ "command": ["get_property", "loop"] }' | socat - /tmp/mpvsocket
                    

                    更多使用方法,参考: https://man.archlinux.org/man/community/mpv/mpv.1.en

                    1 Reply Last reply Reply Quote Share 1
                    • Moved from MR Series by  whycan whycan 
                    • N
                      nikoladi LV 3 last edited by

                      @whycan 在 动手让LVGL8带的Music-Demo音乐响起来(代码已上传) 中说:

                      mpv --quiet --no-terminal --no-video --idle=yes --term-status-msg= --input-ipc-server=/tmp/mpvsocket

                      为什么我这边跑起来了代码,但是点击播放以后,歌曲总是跳到下一首啊?

                      whycan 2 Replies Last reply Reply Quote Share 0
                      • whycan
                        whycan晕哥 LV 9 @nikoladi last edited by

                        @nikoladi
                        是的,楼主最新的代码不小心被删找不到了,这个是早期的代码,有点小bug。

                        1 Reply Last reply Reply Quote Share 0
                        • whycan
                          whycan晕哥 LV 9 @nikoladi last edited by

                          @nikoladi

                          这是作者昨天深夜发我的代码,我试过了,按钮很好使了,但是偶尔还是会die,您试一试: music_player_base_mpv_20220618.7z

                          whycan 1 Reply Last reply Reply Quote Share 0
                          • whycan
                            whycan晕哥 LV 9 @whycan last edited by

                            @whycan
                            245af0d6-63c2-493d-9ab4-5d742d457d6f-image.png

                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@797]:music id:1
                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@798]:music name:1.wav
                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@338]:{ "command": ["loadfile", "./music/1.wav"] }
                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@797]:music id:2
                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@798]:music name:2.wav
                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@338]:{ "command": ["loadfile", "./music/2.wav"] }
                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@797]:music id:3
                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@798]:music name:3.wav
                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@338]:{ "command": ["loadfile", "./music/3.wav"] }
                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@797]:music id:1
                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@798]:music name:1.wav
                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@338]:{ "command": ["loadfile", "./music/1.wav"] }
                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@797]:music id:2
                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@798]:music name:2.wav
                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@338]:{ "command": ["loadfile", "./music/2.wav"] }
                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@797]:music id:3
                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@798]:music name:3.wav
                            [lv_demos/src/lv_demo_music/lv_demo_music_main.c@338]:{ "command": ["loadfile", "./music/3.wav"] }
                            Segmentation fault (core dumped)
                            
                            N 1 Reply Last reply Reply Quote Share 0
                            • N
                              nikoladi LV 3 @whycan last edited by

                              @whycan 我测试可以正常播放了。研究一下代码👍

                              1 Reply Last reply Reply Quote Share 0
                              • U
                                uuuuid LV 6 last edited by

                                b2206aad-1ad1-4bc5-9b58-8ceb49a69a77-image.png

                                本来还想用 F1C200s把楼主的demo跑起来, 发现mpv播放wav文件cpu占用率90%+

                                没法玩了.

                                U 1 Reply Last reply Reply Quote Share 0
                                • U
                                  uuuuid LV 6 @uuuuid last edited by whycan

                                  @uuuuid

                                  命令行没有问题,后台启动mpv:

                                  mpv --quiet --no-terminal --no-video --idle=yes --term-status-msg= --input-ipc-server=/tmp/mpvsocket
                                  

                                  加入歌单:

                                  echo '{ "command": ["loadfile", "./music/a1.wav", "append"] }' | socat - /tmp/mpvsocket
                                  

                                  设置当前播放音乐:

                                  echo '{ "command": ["set_property", "playlist-pos", 0] }' | socat - /tmp/mpvsocket
                                  

                                  音乐属性:

                                  echo '{ "command": ["get_property", "audio-params"] }' | socat - /tmp/mpvsocket
                                  
                                  {"data":{"samplerate":352800,"channel-count":2,"channels":"stereo","hr-channels":"stereo","format":"floatp"},"error":"success"}
                                  

                                  读音乐采样率:

                                  echo '{ "command": ["get_property", "audio-params/samplerate"] }' | socat - /tmp/mpvsocket
                                  
                                  {"data":352800,"error":"success"}
                                  

                                  停止播放:

                                  echo '{ "command": ["set_property", "pause", true] }' | socat - /tmp/mpvsocket
                                  

                                  清空播放列表:

                                  $echo '{ "command": ["playlist-clear"] }' | socat - /tmp/mpvsocket
                                  
                                  {"data":null,"error":"success"}
                                  

                                  获取播放列表:

                                  echo '{ "command": ["get_property", "playlist"] }' | socat - /tmp/mpvsocket
                                  
                                  1 Reply Last reply Reply Quote Share 0
                                  • Y
                                    yn710305799 LV 1 last edited by

                                    请问楼主运行工程,最近也在参考这个DEMO让手机音乐传输给板子

                                    1 Reply Last reply Reply Quote Share 0
                                    • Q
                                      qwer1 LV 1 last edited by

                                      大佬们是怎么运行得的?

                                      1 Reply Last reply Reply Quote Share 0
                                      • S
                                        soso90 LV 7 @btgod001 last edited by

                                        @btgod001 在 动手让LVGL8带的Music-Demo音乐响起来(代码已上传) 中说:

                                        这是把MPV移植到D1 tina了吗? 有移植包吗?

                                        同问~~ 难道是用buildroot编译~~

                                        1 Reply Last reply Reply Quote Share 0
                                        • U
                                          uuuuid LV 6 last edited by uuuuid

                                          获取mp3文件封面:

                                          ffmpeg -i "0376.梅艳芳 - 心肝宝贝.mp3" -vcodec copy -an cover封面.jpg
                                          

                                          参考: https://stackoverflow.com/questions/13592709/retrieve-album-art-using-ffmpeg

                                          1 Reply Last reply Reply Quote Share 0
                                          • S
                                            swxg2008 LV 1 last edited by

                                            各位大佬在用的时候没有发现内存泄漏吗?好像是mpv导致的,有没有解决办法啊

                                            1 Reply Last reply Reply Quote Share 0
                                            • 1 / 1
                                            • First post
                                              Last post

                                            Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号

                                            行为准则 | 用户协议 | 隐私权政策