Найти тему

Создаем свой медиаплеер на базе библиотеки vlc lib с нуля

Приветствую вас дамы, господа, ожившие механизмы, ксеносы и другие, mynameisinfinityandilivetits и сегодня мы создадим свой медиаплеер на базе готовых боярских библиотек от плеера vlc (https://www.videolan.org/vlc/).

Для этого нам понадобится какой нибудь компилятор (библиотека портированна на все нормальные языки программирования), и немного упорства, чтобы прочитать эту статью. Ну или чтобы скопипастить (ссылку на все исходники с готовым проектом я добавлю в конце).

Итак ближе к делу. Я буду использовать лазарус (кроссплатформная среда разработки для фрипаскаля), так как мне лень лишний раз возится, но логику объясню вне конкретного языка, для твоей системы и языка исходники тут (https://www.videolan.org/vlc/libvlc.html). В частности для джава (https://github.com/caprica/vlcj) и сиплюсплюс (https://code.videolan.org/videolan/libvlcpp). Для паскаля сурсы будут в моем проекте, но ссылка также (https://wiki.videolan.org/Using_libvlc_with_Delphi/) тут.

Создаем новый проект десктопного приложения, и сразу добавляем формы, в моем случае я добавил сразу 5 форм (в идеале даже 6 или больше), для области рисования видео, для плейлиста, кнопок управления, меню и тд. Так как мой интерфейс будет содержать всякие финтефлюшки.

По сути все эти формы у меня будут составными компонентами одного окна, я делаю так для более простого восприятия кода и эффекта прозрачности с плавным переходом без изобретения велосипеда.

У всех окон я убираю стандартный бордюр (так как любой колхозный плеер имеет свои, это канон), и добавляю на форму 6 панелек и таймер, панели размещаю и привязываю так чтобы получился бордюр окна с заголовком, но только в 1 пиксель.

Теперь нужно реализовать их функционал, для изменения позиции окна плеера на экране и его разера. Для этого я создаю 3 глобальных переменных (две для координат, одну вспомогательную для определения направления и способа изменения размера, так как весь код я помещу в таймер).

Для всех панелек я создаю по два стандартных события, событие нажатия клавиши мыши, и ее отпускания. При нажатии где то на хоне панели мы будем включать таймер и отправлять в глобальную переменную условный текст, по которому будем определять че и куда надо изменять. По событии отпускания.

  1. // вспомогательная переменная для получения координат курсора
  2. help_last_resize_position:TPoint;
  3. // еще одна, для получения размера окна плеера и его позиции
  4. help_last_resize_position2:TPoint;
  5. // а эта для таймера, чтобы определять какие манипуляции надо делать
  6. rsz_move_type:string;

И на самом деле у меня для всех событий отпускания клавиши одна процедура\функция\события, которое я просто создал для одной панельки, и потом присвоил эту процедуру в качестве события отпускания для всех, так как она просто отключает таймер.

Для каждой конкретной панельки нам нужны свои данные, а именно координаты мыши в момент срабатывания события (их отдает сама процедура, но можно использовать функцию виндоус GetCursorPos(); где в качестве параметра переменная для получения координат, типа координаты).

В принципе можем также собирать всю информацию разом и копипастить код (или вынести в отдельную функцию, если ты фригидный дурачек). И изменять только содержимое нашей вспомогательной переменной для таймера, чтобы определять тип изменения размера.

  1. // процедура для обработки простого перемещения окна по экрану
  2. // типа тык по заголовку окна
  3. procedure TMP.BRDTRMouseDown(Sender: TObject; Button: TMouseButton;
  4. Shift: TShiftState; X, Y: Integer);
  5. begin
  6. // получаем во вспомогательную переменную
  7. // координаты тыка мыши по оси Х
  8. help_last_resize_position.X:=X;
  9. // получаем во вспомогательную переменную
  10. // координаты тыка мыши по оси У
  11. help_last_resize_position.Y:=Y;
  12. // получаем в другую вспомогательную переменную
  13. // отступ слева (отступ окна плеера, от левого
  14. // края экрана, в пикселях), к которому прибавляем
  15. // ширину окна плеера
  16. help_last_resize_position2.X:=MP.Left+MP.Width;
  17. // получаем в другую вспомогательную переменную
  18. // отступ сверху (отступ окна плеера, от верхнего
  19. // края экрана, в пикселях), к которому прибавляем
  20. // высоту окна плеера
  21. help_last_resize_position2.Y:=MP.Top+MP.Height;
  22. // получаем во вспомогательную переменную тип
  23. // манипуляции с окном программы, в моем случе
  24. // по слову top я буду определять что это простое
  25. // перемещение окна программы
  26. rsz_move_type:='top';
  27. // включаем наш таймер, который был изначально выключен
  28. Rsz.Enabled:=true;
  29. end;

Это только пример одной панельки, но в исходниках будут все, чтобы лишни раз не забивать голову, другие операции мы выполняем при помощи убогой математики, и перемещения окна программы, к примеру

ШиринаОкнаПрограммы = ШиринаОкнаПрограммы  + (ТекущиеКоординатыМышиПоОсиХ - (ОтступОкнаПрограммыОтЛевогоКраяЭкрана+ШиринаОкнаПрограммы  ));

Или пример для изменения размера справа, где мы изменяем положение отступа окна слева и ширину

ОтступОкнаПрограммыОтЛевогоКраяЭкрана = ТекущиеКоординатыМышиПоОсиХ -КоординатыМышиВМоментНачалаИзмененияРазмера;

ШиринаОкнаПрограммы :=ОтступОкнаПрограммыОтЛевогоКраяЭкрана + ШиринаОкнаПрограммы + (КоординатыМышиВМоментНачалаИзмененияРазмера -ТекущиеКоординатыМышиПоОсиХ );

Как то так. Чтобы не забивать голову вот мой код.

  1. // волшебный код который не требует комментариев
  2. if rsz_move_type = 'move' then
  3. begin
  4. MP.Left:=Mouse.CursorPos.X-help_last_resize_position.X;
  5. MP.Top:=Mouse.CursorPos.Y-help_last_resize_position.Y;
  6. end;
  7. if rsz_move_type = 'right_bottom' then
  8. begin
  9. MP.Width:=MP.Width + (Mouse.CursorPos.X - (MP.Left+MP.Width));
  10. MP.Height:=MP.Height + (Mouse.CursorPos.Y - (MP.Top+MP.Height));
  11. end;
  12. if rsz_move_type = 'right' then
  13. begin
  14. MP.Width:=MP.Width + (Mouse.CursorPos.X - (MP.Left+MP.Width));
  15. end;
  16. if rsz_move_type = 'bottom' then
  17. begin
  18. MP.Height:=MP.Height + (Mouse.CursorPos.Y - (MP.Top+MP.Height));
  19. end;
  20. if rsz_move_type = 'left' then
  21. begin
  22. MP.Left:=Mouse.CursorPos.X-help_last_resize_position.X;
  23. MP.Width:=help_last_resize_position2.X+(help_last_resize_position.X-Mouse.CursorPos.X);
  24. end;
  25. if rsz_move_type = 'top' then
  26. begin
  27. MP.Top:=Mouse.CursorPos.Y-help_last_resize_position.Y;
  28. MP.Height:=help_last_resize_position2.Y+(help_last_resize_position.Y-Mouse.CursorPos.Y);
  29. end;
  30. // помимо изменения размера и положения окна плеера
  31. // перетаскиваю вслед за ним и задаю новые размеры для
  32. // плейлистов, панели кнопок плеера и тд
  33. TopMenu.Left:=MP.Left+1;
  34. TopMenu.Top:=MP.Top+1;
  35. TopMenu.Width:=MP.Width-2;
  36. TopMenu.Visible:=true;
  37. TopMenu.BringToFront;
  38. PlayerControls.Left:=MP.Left+1;
  39. PlayerControls.Top:=MP.Top+(MP.Height-51);
  40. PlayerControls.Width:=MP.Width-2;
  41. PlayerControls.Visible:=true;
  42. PlayerControls.BringToFront;
  43. if PMenu.Visible = true then
  44. begin
  45. PMenu.Left:=MP.Left;
  46. PMenu.Top:=MP.Top+51;
  47. PMenu.Height:=MP.Height-102;
  48. PMenu.Width:=400;
  49. end;
  50. if Playlist.Visible = true then
  51. begin
  52. Playlist.Visible:=true;
  53. Playlist.Left:=(MP.Left+MP.Width)-Playlist.Width;
  54. Playlist.Top:=MP.Top+51;
  55. Playlist.Height:=MP.Height-102;
  56. end;

Теперь когда у нас есть окно плеера подтянем картинки со звуками на экран, подключением библиотеки lib_vlc.dll. Все функции из этой библиотеки интуитивно понятны и одинаковы для всех языков. Полный список функций из библиотек можно посмотреть на сайте (https://videolan.videolan.me/vlc/group__libvlc.html).

Подключаем исходники связывающие библиотеку с описанными функциями-заголовками (для паскаля это uses PasLibVlcUnit). Создаем две глобальных переменных, первая переменная типа libvlc_instance_t_ptr, вторая переменная типа libvlc_media_player_t_ptr, для экземпляра библиотеки и плеера, название может быть любым, у меня они называются из примеров p_li и p_mi (неудачные сокращения от либрарей инстанс и медиа инстанс).

Создаем функцию, в который выполняем загрузку и инициацию экземпляра библиотеки, и из нее уже вызываем функцию, которая создаст нам экземпляр готового плеера.

  1. function INITVLC:boolean;
  2. begin
  3. // загружаем библиотеку и линкуем заголовки
  4. // для того чтобы эта функция работала корректно
  5. // на компьютере должен быть vlc media player
  6. // я же использую ее так как у меня кривая сборка
  7. // винды, в которой не регистрируются дллешки
  8. libvlc_dynamic_dll_init();
  9. // загружаем библиотеку из какого нибудь места
  10. // в качестве аргумента функции я передаю
  11. // содержимое моей глобальной переменной app_path,
  12. // в которую я поместил путь до моей программы,
  13. // к которой добавляю название библиотеки с плеером
  14. //libvlc_dynamic_dll_init_with_path(app_path+'libvlc.dll');
  15. // создаю условие, если содержимое переменной
  16. // libvlc_dynamic_dll_error из PasLibVlcUnit
  17. // содержит что то большее чем ничего, вызываю
  18. // стандартное диалоговое окно системы, и вывожу
  19. // код ошибки из libvlc_dynamic_dll_error
  20. if (libvlc_dynamic_dll_error <> '') then
  21. begin
  22. // вызываю диалог
  23. MessageDlg(libvlc_dynamic_dll_error, mtError, [mbOK], 0);
  24. // уничтожаю экземпляр программы
  25. // и завершаю работу приложения
  26. Application.Terminate();
  27. // экстренно прирываю выполнение дальнейшего кода
  28. exit;
  29. end;
  30. // с объектом библиотеки PasLibVlcUnit
  31. // создаю новый экземпляр перменной, в
  32. // которую помещаю дополнительные параметры
  33. // для загрузки библиотеки
  34. with TArgcArgs.Create([
  35. WideString(libvlc_dynamic_dll_path),
  36. '--intf=dummy',
  37. '--ignore-config',
  38. '--quiet',
  39. '--no-video-title-show',
  40. '--no-video-on-top'
  41. ]) do
  42. begin
  43. // вызываю функцию из библиотеки PasLibVlcUnit,
  44. // которая создает новый экземпляр условного
  45. // скрытого плеера vlc с аргументами и ассоциирую
  46. // ее с переменной p_li
  47. p_li := libvlc_new(ARGC, ARGS);
  48. // уничтожаю объект TArgcArgs, так как он больше
  49. // не нужен
  50. Free;
  51. end;
  52. // создаю условие, если переменная экземпляра p_li
  53. // содержит что то, кроме ничего продолжаю действия
  54. if (p_li <> NIL) then
  55. begin
  56. // ассоциирую с переменной p_mi результат выполнения
  57. // функции libvlc_media_player_new, в которой передаю
  58. // в качестве аргумента переменную с загруженным
  59. // экземпляром библиотеки lib_vlc.dll
  60. p_mi := libvlc_media_player_new(p_li);
  61. end;
  62. // возвращяю результат работы функции как true
  63. result:=true;
  64. end;

Представим что все понятно и все получилось с первого раза. А еще предположим что мы разместили на форме все финтифлюшки как хотели и они работают корректно. Давайте сразу опишем функции для открытия файла, воспроизведения, паузы, и стопа.

Все проще чем кажется, мы просто используем простенькие функции из нашей линькованной библиотеки, и в качестве аргумента для этих функции передаем нашу глобальную переменную с плеером, или библиотекой, в зависимости от действия.

  1. function StopVideo:boolean;
  2. begin
  3. // создаю условие, если содержимое
  4. // переменной p_mi с медиаплеером
  5. // не равно ничему, выполняю содержимое
  6. // условия
  7. if (p_mi <> NIL) then
  8. begin
  9. // вызываю функцию из библиотеки
  10. // PasLibVlcUnit, которая выполняет паузу,
  11. // в качестве аргумента указываю
  12. // переменную с которой ассоциирован
  13. // экземпляр плеера
  14. libvlc_media_player_pause(p_mi);
  15. // выполняю перерисовку области экрана
  16. // это для моих финтифлюшек, этот код не
  17. // обязателен
  18. MP.Monitor1.Repaint;
  19. MP.Monitor1.ParentBackground:=false;
  20. MP.Monitor1.ParentBackground:=true;
  21. end;
  22. // возвращяю результат выполнения функции как true
  23. result:=true;
  24. end;
  25. function PlayVideo(filename:WideString):boolean;
  26. var
  27. // создаю переменную типа медиаобъект
  28. // из библиотеки PasLibVlcUnit
  29. p_md : libvlc_media_t_ptr;
  30. // слздаю вспомогательную переменную
  31. // для конвертирования имени файла
  32. // типа строка анси
  33. a_st : AnsiString;
  34. // и еще одну типа символ
  35. p_st : PAnsiChar;
  36. begin
  37. // конвертирую содержимое аргумента
  38. // функции PlayVideo в фют8
  39. a_st := UTF8Encode(fileName);
  40. // получаю первый символ из пути,
  41. // который конвертирую в формат анси,
  42. // не совсем понятно для чего это
  43. p_st := PAnsiChar(@a_st[1]);
  44. // создаю условие, если содержимое
  45. // переменной, с которой ассоциированна библиотека
  46. // lib_vlc.dll больше чем ничего, продолжаю действия
  47. if (p_li <> NIL) then
  48. begin
  49. // вызываю функцию из библиотеки PasLibVlcUnit
  50. // которая получает информацию об файле, его тип,
  51. // размер и тд, в качестве параметра передаю
  52. // содержимое вспомогательных переменных, с которыми
  53. // я ассоциировал конвертированный в формат ютф8
  54. // путь к файлу с имененм, результат выполнения
  55. // функции помещяю в другую вспомогательную перменную p_md
  56. p_md := libvlc_media_new_path(p_li, p_st);
  57. // создаю условие, если содержимое
  58. // переменной p_md больше чем ничего,
  59. // продолжаю действия
  60. if (p_md <> NIL) then
  61. begin
  62. // создаю условие, если содержимое
  63. // переменной с экземпляром медиаплеера
  64. // больше чем ничего, продолжаю действия
  65. if (p_mi <> NIL) then
  66. begin
  67. // вызываю функцию из библиотеки PasLibVlcUnit,
  68. // которая позволяет перехватить из программы,
  69. // в библиотеку обработку нажатия клавиш клавиатуры,
  70. // в качестве параметров передаю переменную с медиаплеером
  71. // и указатель 0, так как обрабатывать клавишы
  72. // будет моя программа
  73. libvlc_video_set_key_input(p_mi, 0);
  74. // выполняю аналогичную операцию для мыши
  75. libvlc_video_set_mouse_input(p_mi, 0);
  76. // вызываю функцию из библиотеки PasLibVlcUnit,
  77. // которая позволяет назначить какой нибудь объект,
  78. // у которого есть в своем содержимом область для
  79. // рисования в качестве экрана для видео, в качестве
  80. // параметров передаю переменную с экземпляром плеера,
  81. // и индекс объекта с областью рисования в формате
  82. // системы, у меня это панелька с названием Monitor1
  83. libvlc_media_player_set_display_window(p_mi, MP.Monitor1.Handle);
  84. // вызываю функцию из библиотеки PasLibVlcUnit,
  85. // которая позволяет ассоциировать медиаобъект с
  86. // экземпляром плеера, в качестве параметра передаю
  87. // экземпляр медиплеера из глобальной переменной и
  88. // содержимое вспомогательной переменной с информацией
  89. // о медиафайле
  90. libvlc_media_player_set_media(p_mi, p_md);
  91. end;
  92. // вызываю функцию из библиотеки PasLibVlcUnit,
  93. // которая позволяет определить и декодировать файл
  94. libvlc_media_release(p_md);
  95. end;
  96. end;
  97. // возвращяю результатом выполнения функции true
  98. result:=true;
  99. end;

И наконец функция для начал воспроизведения. У меня эта функция объеденяет в себе возможности воспроизводить\пауза, так как у плеера может быть несколько состояний (файл загружен, файл не найден, файл воспроизводиться и тд), необходимо обработать их все. Опишу для состояния пауза, так как остальные интуитивно понятны.

  1. procedure TPlayerControls.Image1Click(Sender: TObject);
  2. begin
  3. // создаю условие если результат выполнения
  4. // функции из библиотеки PasLibVlcUnit под названием
  5. // libvlc_media_player_get_state вернул значение,
  6. // которое соответствует условному ключевому слову
  7. // из библиотеки libvlc_Paused, выполняю действия условия
  8. if libvlc_media_player_get_state(p_mi) = libvlc_Paused then
  9. begin
  10. // вызываю функцию воспроизведения
  11. libvlc_media_player_play(p_mi);
  12. // это финтефлюшки, изменяю иконку кнопки
  13. // воспроизведение с воспроизводить на паузу
  14. Self.Image1.Picture:=Self.src_img_pause.Picture;
  15. // внезапно прерываю выполнение процедуры
  16. exit;
  17. end;
  18. if libvlc_media_player_get_state(p_mi) = libvlc_Playing then
  19. begin
  20. libvlc_media_player_pause(p_mi);
  21. Self.Image1.Picture:=Self.src_img_play.Picture;
  22. exit;
  23. end;
  24. if libvlc_media_player_get_state(p_mi) = libvlc_Ended then
  25. begin
  26. libvlc_media_player_play(p_mi);
  27. Self.Image1.Picture:=Self.src_img_pause.Picture;
  28. exit;
  29. end;
  30. if libvlc_media_player_get_state(p_mi) = libvlc_Stopped then
  31. begin
  32. libvlc_media_player_play(p_mi);
  33. Self.Image1.Picture:=Self.src_img_pause.Picture;
  34. exit;
  35. end;
  36. end;

И вот у нас уже есть готовый прототип программы, который может воспроизводить видео, осталось создать кнопку и разместить диалоговое окно открытия файла, внутри события кнопки открыть, вызвать нашу функцию, для открытия медиафайла, и передать ей результат выполнения диалогового окна для выбора файла.

Теперь давайте добавим полосочку таймера, и плейлист. Для полоски таймера я использую еще две панельки, одна над другой, разиных цветов, по мере продвижения времени, я буду изменять размер одной полоски, которая будет находится над другой, другая будет представлять собой всю временную шкалу.

Плейлист я размещу в отдельном окне и возьму для него стандартную заготовки из системных библиотек TListBox (в с++ cli это ListBox как не парадоксально, и JList вjdk). Еще добавлю новый таймер, который будет получать текущую позицию времени воспроизводимого файла, и обновлять прогресс панельки со временем.

Давайте опишем функцию таймера, так как он будет работать каждую секунду все время и проверять состояние плеера, исходя из чего выполнять перерасчет прогресса временной шкалы.

Для начала у меня есть вспомогательная функция, которая конвертирует секунды в привычный формат, так как у меня отображается еще и время видео. Она не обязательна, но сурс просто добавлю чтобы бы.

  1. function ConvertMsToNormalTime(i:TCaption):TCaption;
  2. var a:string;
  3. b:real;
  4. c,d:integer;
  5. min,sec,hours:integer;
  6. begin
  7. a:='';
  8. c:=0;
  9. d:=round(StrToFloat(i));
  10. min:=0;
  11. sec:=0;
  12. hours:=0;
  13. while c < d do
  14. begin
  15. inc(sec);
  16. if sec = 60 then
  17. begin
  18. inc(min);
  19. sec:=0;
  20. end;
  21. if min = 60 then
  22. begin
  23. inc(hours);
  24. min:=0;
  25. end;
  26. inc(c);
  27. end;
  28. if hours < 10 then
  29. begin
  30. a:=a+'0'+IntToStr(hours)+':';
  31. end else
  32. begin
  33. a:=a+IntToStr(hours)+':';
  34. end;
  35. if min < 10 then
  36. begin
  37. a:=a+'0'+IntToStr(min)+':';
  38. end else
  39. begin
  40. a:=a+IntToStr(min)+':';
  41. end;
  42. if sec < 10 then
  43. begin
  44. a:=a+'0'+IntToStr(sec);
  45. end else
  46. begin
  47. a:=a+IntToStr(sec);
  48. end;
  49. result:=a;
  50. end;

Теперь сама функция таймера-обновлятеля прогресса временной шкалы.

  1. procedure TMP.MoveTimerTimer(Sender: TObject);
  2. // создаю вспомогательные переменные типа
  3. // реальное число (для быдлокодеров поясню что
  4. // это число с плавающей запятой)
  5. var a,s1,s2,s3:real;
  6. // создаю вспомогательные переменные типа число
  7. // (целое число)
  8. var b,c:integer;
  9. begin
  10. // создаю условие, если с переменной
  11. // для экземпляра ассоциированно что
  12. // то что больше ничего, выполняю код
  13. // условия
  14. if (p_mi <> NIL) then
  15. begin
  16. // создаю условие, если функция
  17. // libvlc_media_player_get_state для
  18. // получения статуса плеера из
  19. // библиотеки PasLibVlcUnit возвращяет
  20. // значение, которое соотвествует
  21. // описанному в библиотеке PasLibVlcUnit
  22. // libvlc_Playing значение, выполняю код условия
  23. if (libvlc_media_player_get_state(p_mi) = libvlc_Playing) then
  24. begin
  25. // ассоциирую со вспомогательной
  26. // переменной значени 0
  27. b:=0;
  28. // начинаю цикл с условием
  29. // повторять до тех пор, пока
  30. // значение переменной b меньше 100
  31. while b < 100 do
  32. begin
  33. // со вспомогательной переменной
  34. // ассоциирую результат выполнения функции
  35. // libvlc_media_player_get_position, которая
  36. // позволяет получить текущую позицию в милисекундах
  37. // воспроизводимого видео, из библиотеки PasLibVlcUnit,
  38. // в качестве параметра передаю глобальную переменную
  39. // с которой ассоциирован экземпляр плеера, и конвертируя
  40. // встроенной функцией паскаля Real привожу данные к типу
  41. // число с плавующей точкой, которое округляю до секунд
  42. s1:=RoundTo(Real(libvlc_media_player_get_position(p_mi)),-3);
  43. // создаю условие, если 1 деленный
  44. // на 100 умноженный на содержимоей
  45. // вспомогательной переменной b больше
  46. // значения s1 выполняю условие, иначе
  47. // выполняю альтернативное действие
  48. // (функция libvlc_media_player_get_position
  49. // возвращяет значение в диапозоне от 0.0 до
  50. // 1.0)
  51. if ((1 / 100) * Real(b) >= s1) then
  52. begin
  53. // переношу значение переменной b
  54. // в другую вспомогательную переменную c
  55. // и прекращяю цикл
  56. c:=b;
  57. b:=100;
  58. end ELSE
  59. begin
  60. c:=100;
  61. end;
  62. inc(b);
  63. end;
  64. // для панельки прогресса задаю ширину,
  65. // которая равна округленному процентному
  66. // соотношению всей временной шкалы,
  67. // которая у меня представлена в другой
  68. // панельке
  69. PlayerControls.Time2.Width:=Round((PlayerControls.Time1.Width / 100) * c);
  70. // в надпись (или обхект Textlabel) помещаю результат
  71. // выполнения вспомогательной функции для конвертирования
  72. // секунд в привычный формат, в которую передаю (конвертируя
  73. // в текст значение с плавующей точкой), которое получаю
  74. // из функции библиотеки PasLibVlcUnit, которая возвращяет
  75. // текущее время воспроизведения в милисекундах
  76. PlayerControls.Label1.Caption:=ConvertMsToNormalTime(FloatToStr(libvlc_media_player_get_time(p_mi) / 1000));
  77. // аналогичную операцию проделываю для другой надписи,
  78. // в которой находится полное время видео
  79. PlayerControls.Label2.Caption:=ConvertMsToNormalTime(FloatToStr(Real(libvlc_media_player_get_length(p_mi)) / 1000))+' ('+IntToStr(c)+'%)';
  80. end;
  81. // создю условие, если результат выполнения функции
  82. // libvlc_media_player_get_state возвращяет значение соотвествующие
  83. // libvlc_Ended из библиотеки PasLibVlcUnit, выполняю действия условия
  84. if libvlc_media_player_get_state(p_mi) = libvlc_Ended then
  85. begin
  86. // задаю ширину панельки прогресса, равную ширине
  87. // всей панельки времени
  88. PlayerControls.Time2.Width:=PlayerControls.Time1.Width;
  89. // езе раз получаю текущее время воспроизведения
  90. // видео (позицию воспроизведения)
  91. PlayerControls.Label1.Caption:=ConvertMsToNormalTime(FloatToStr(libvlc_media_player_get_time(p_mi) / 1000));
  92. // отключаю таймеры моих финтифлюшек
  93. // этот код не обязателен
  94. MP.MoveTimer.Enabled:=false;
  95. MP.Player_Next.Enabled:=true;
  96. end;
  97. end;
  98. end;

Ну и в дополнение код для события тыка по прогресс бару. Для панельки, которая отвечает за всю временную шкалу создаем событие тыка, после эту же функцию ассоциируем с аналогичным событием второго панельки, которая отвечает за текущее время.

  1. procedure TPlayerControls.Time1Click(Sender: TObject);
  2. // создаем вспомогательные переменные
  3. // типа целое число
  4. var b,c:integer;
  5. begin
  6. // со вспомогательной переменной ассоциируем
  7. // координаты курсора по оси Х, из которых
  8. // вычитаем все отступы от левого края экрана,
  9. // до нашей панельки с прогрессом
  10. c:=Mouse.CursorPos.X - (1+MP.Left+Time1.left);
  11. // начинаем цикл для получения процентного
  12. // значения панельки воспроизведения
  13. b:=0;
  14. while b <= 99 do
  15. begin
  16. // создаем условие, если конвертированное
  17. // в число с плавующей точкой значение
  18. // ширины панельки текущего времени деленное
  19. // на 100 и умноженное на значение переменной b
  20. // больше или равно значениею переменной c
  21. // выполняем дийствия условия
  22. if Real(Time1.Width / 100) * b >= c then
  23. begin
  24. // из библиотеки PasLibVlcUnit вызываем функцию для
  25. // изменения времени воспроизведения, в качестве параметра
  26. // указываем переменную с которой ассоциирован экземпляр
  27. // плеера и новое время в формате числа с плавующей точкой,
  28. // которое получаем из умножения 1 на значение переменной b
  29. libvlc_media_player_set_position(p_mi, StrToFloat(FloatToStr(Real(1) / Real(99) * b)));
  30. // вызываем функцию для воспроизведения
  31. // видео, из библиотеки PasLibVlcUnit, которая
  32. // начинает воспроизведение видео, в качестве параметра
  33. // указываем переменную с которой ассоциирован экземпляр
  34. libvlc_media_player_play(p_mi);
  35. // b = 100
  36. b:=100;
  37. end;
  38. // b = b + 1;
  39. inc(b);
  40. end;
  41. end;

Вот у нас есть уже рабочий плеер, и в нем даже есть временная шкала. Давайте я еще немного расскажу про плейлист и в принципе альфа версия плеера будет готова. Так как у меня плеер планировался преимущественно как видеоплеер, как альтернатива тому же vlc media player, с функционалом больше приближенным к ютюбу и вообще интерфейсом веб плеера, у меня нет в программе получения расширенной информации о медиа.

Это получить не так сложно, тем более что vlc уже все сам сделал, просто нужно взять, написав соответствующую функцию-запрос (вики по vlc lib в помощь). У меня плейлист будет содержать информацию о размере файла, имя файла и расположение.

При этом во время загрузки первого файла, как и WMP, мой плеер ищет все другие воспроизводимые форматы в той же папке и помещает их в плейлист. Насколько мне известно vlc работает со всеми основными форматами медиа.

В моей программе планировался совмещенный режим с просмотром фотографии, поэтому у меня список немного другой.

*.mp4;*.mp3;*.mp2;*.wmv;*.wma;*.avi;*.asf;*.mov;*.mkv;*.wav;*.ogg;*.ogm;*.flac;*.flv;*.mxf;*.smf;*.mid;*.3gp;*.jpg;*.jpeg;*.png;*.gif;*.bmp;*.ico;*.tif;*.tiff;

В лазарусе для этого есть функция FindAllFiles(), у вас может быть другая (скорее всего стандартный интерфейс с результирующим листом, или функция FindFirst, FindNext). Я просто помещаю в список листа все полные пути к файлам, которые перерисовываю в событии отрисовки элементов списка TListBox.

  1. procedure TPlaylist.ListBox1DrawItem(Control: TWinControl; Index: Integer;
  2. ARect: TRect; State: TOwnerDrawState);
  3. // создаю числовую переменную
  4. var b:integer;
  5. //a:TLCLTextMetric;
  6. //var f:file;
  7. begin
  8. //AssignFile(f, ListBox1.Items[Index]);
  9. //Draw(0,0,Playlist.img_icon_music.Picture.Graphic);
  10. // с объектом канвас у ListBox начинаю действия
  11. with ListBox1.Canvas do
  12. begin
  13. // создаю условие, если значение
  14. // передаваемого аргумента с текущим
  15. // индексом в листе, которое, деленное
  16. // имеет остаток 0, выполняю условия,
  17. // иначе перехожу к альтернативному условию
  18. if (Index) mod 2 = 0 then
  19. begin
  20. // у объхекта канвас листбокса1
  21. // свойства цвета фона закраски задаю
  22. // из глобальной вспомогательной переменной,
  23. // в которой содержится код цвета
  24. Brush.Color := style_base_color;
  25. // аналогично для цвета отрисовки
  26. Font.Color := style_text_color;
  27. // заполняю фоновым цветом
  28. // всю область отрисовки
  29. FillRect(ARect);
  30. // для текстовой отрисовки
  31. // задаю размер шрифта в 10
  32. Font.Size:=10;
  33. // отрисовываю текст в координатах,
  34. // которые были получены в качесте аргумента,
  35. // отрисовываю имя файла, полученное из
  36. // значения строки списка, которое пропускаю
  37. // через функцию, которая отрезает из названия
  38. // расширение и путь (то есть оставляет только имя)
  39. TextOut(ARect.Left+4, ARect.Top, GetFileNameFromFullPath(ListBox1.Items[Index]));
  40. // для текстовой отрисовки
  41. // задаю размер шрифта 6
  42. Font.Size:=6;
  43. // отрисовываю в сдвинутых координатах, относительно
  44. // и внутри области отрисовки, отрисовываю полный
  45. // путь к файлу с имененм
  46. TextOut(ARect.Left+4, ARect.Top+16, ListBox1.Items[Index]);
  47. end else
  48. begin
  49. // Альтернативное условие,
  50. // делаю все тоже саоме,
  51. // олько инвертирую цвета отрисовки
  52. // и заливки
  53. Brush.Color := style_text_color;
  54. FillRect(ARect);
  55. Font.Color := style_base_color;
  56. Font.Size:=10;
  57. TextOut(ARect.Left+4, ARect.Top, GetFileNameFromFullPath(ListBox1.Items[Index]));
  58. Font.Size:=6;
  59. TextOut(ARect.Left+4, ARect.Top+16, ListBox1.Items[Index]);
  60. end;
  61. end;
  62. end;

Ну и в качестве бонуса модная нынче лгбт подцветка, которая у меня реализована только на заголовок с названием файла, так как выглядит как мне показалось колхозно, и для вырезания годных цветов для переливания как на кулерах лень делать. У меня он выключен, поэтому нужно включить таймер PlayerRGB.

Ссылка на сурсы (https://1drv.ms/u/s!Att7piQftCNFgSAEPOUhgXc0jR5R?e=F2CALy)

В общем на этом я думаю можно закончить, так как альфа плеера по сути описана, а что то дополнительное для слабаков. Надеюсь данная статья будет полезна, поделись своим мнением и если есть какие либо ошибки и предложения, ты всегда можешь дополнить меня в комментах, спасибо за внимание^^