Иногда мы хотим поделиться с друзьями частью какого то видео на YouTube — время концентрации внимания в современной реальности снижено до предела, и если скидывать ссылку на ролик(даже с таймкодом начала) с комментарием «смотреть с 21:51 по 24:55» — велика вероятность, что видео просмотрено не будет.

Кроме того — куски видео могут потребоваться для монтажа своих роликов — и довольно неудобно скачивать ради нескольких секунд весь ролик и искать/вырезать нужную часть в программе для монтажа.

Как загружать часть видео YouTube при помощи ffmpeg — под катом

Получаем прямую ссылку


Часть реализации моего Telegram бота на Python:
Нам потребуется библиотека pytube.
Создаем объект класса YouTube, которому передаем нашу ссылку, передаем номер нужного потока и получаем прямую ссылку на видео/аудио

from pytube import YouTube
link = "ссылка на видео"
itag = номер_потока
url = YouTube(link).streams.get_by_itag(itag).url

Обратите внимание, что потоки 1080p и 480p не имеют аудиодорожки.


Далее — подаем ссылку на вход (-i) ffmpeg вместе с таймкодами начала (-ss) и конца (-to) в формате «hh:mm:ss.xx». Задаем аудио кодек, битрейт и "-avoid_negative_ts make_zero" для того чтобы избежать подвисания картинки в начале видео из за потери ключевых кадров.

ffmpeg загрузит видео с нужного момента — нам не нужно качать видео на компьютер и обрезать — мы сразу выкачиваем нужный кусок.

process_call_str = 'ffmpeg -ss {1} -to {2} -i "{0}"'                            '-acodec aac -b:a 192k -avoid_negative_ts make_zero "{3}"'
                            .format(str(url), str(ss), str(t), download_file_path)
status = subprocess.check_call(process_call_str, shell=True)

Потоки без аудио


А что же делать с потоками без аудио? FFMPEG выручит нас и здесь — он может принимать на вход несколько потоков и объединять их.

Получаем прямую ссылку на видеопоток (например 137 — 1080p) и на поток с аудио/видео — например 18 — 360p

url = YouTube(link).streams.get_by_itag(itag).url
aurl = YouTube(link).streams.get_by_itag(18).url

Далее начинается магия — подаем на вход оба потока и при помощи "-map" берем видео дорожку из первого потока и аудио дорожку из второго потока и объединяем- теперь загрузка и объединение потоков происходит с нужного места из двух источников.

process_call_str = 'ffmpeg -ss {2} -to {3} -i "{0}" -ss {2} -to {3} -i "{1}"'                             ' -acodec aac -b:a 192k -avoid_negative_ts '                             'make_zero -map 0:v:0 -map 1:a:0 "{4}"'                             .format(str(url), str(aurl), str(ss), str(t), download_file_path)
status = subprocess.check_call(process_call_str, shell=True)

Заключение


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

При помощи ffmpeg в боте реализовано ускорение/замедление звука с тонокомпенсацией, сжатие в формат opus. Теперь вот и загрузка видео/аудио с нужного момента по таймкодам — достаточно прикрепить к ссылке таймкоды и бот оперативно пришлет нужный кусок аудио/видео в нужном формате и качестве:

http://www.youtube.com/watch?v=Qgm36HHDEk0(30:29.5-30:38.5)

Комментарии (14)


  1. vladfaust
    17.03.2018 16:49
    +1

    Спасибо, пригодится для одного из моих проектов!


  1. AllexIn
    17.03.2018 22:04
    +1

    Вообще ffmpeg довольно мощная штука

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


    1. SlavikMIPT Автор
      17.03.2018 22:11

      ага — я прямо прифигел что он умеет делать) до этого приходилось качать файл и на сервере уже с ними работать, а оказывается можно все делать одним инструментом, одной командой, при этом алгоритмы оптимизированные. Единственная сложность — скомпилировать ffmpeg может быть)


      1. Nicodinus
        18.03.2018 17:20

        И даже для компиляции есть простой скрипт который умеет инклудить много проприетарных либ внутрь себя (раньше было особенно актуально для nvenc, ибо его не было ещё в коде родного ffmpeg'а). Разве что кофе на варит) Разработан кажется хабравчанином, только ник запамятовал. Вот ссылка (https://github.com/rdp/ffmpeg-windows-build-helpers), надеюсь не сочтут за рекламу.


  1. valera5505
    18.03.2018 00:40

    Это (и многое другое) умеет youtube-dl. Но самому написать, конечно, интереснее :)


    1. Taciturn
      18.03.2018 00:50

      Это просто прекрасно! Не могли бы вы зайти в ишью #622 (открыт с 2013 года) и рассказать там всем как именно youtube-dl всё сам умет, а то там всё тоже сводится к ffmpeg.


      1. Zverik
        18.03.2018 14:03

        Обрезать видеофайл в произвольном формате — не задача youtube-dl. В этом же тикете приведён пример:

        ffmpeg -ss 3:59:10 -i $(youtube-dl -f 22 -g 'https://www.youtube.com/watch?v=mMZriSvaVP8') -t 3:06:40 -c copy react-spot.mp4

        То же самое делает автор статьи, только вместо youtube-dl пользуется какой-то питоновской библиотекой. Библиотека оказалась не очень очевидна из консоли, в отличие от youtube-dl, поэтому появилась целая статья на хабре с предложением читать xml и вызывать ffmpeg изнутри питоновского скрипта.


        1. Taciturn
          18.03.2018 14:13

          Статья называется «FFMPEG. Загружаем часть видео с YouTube». Она, в первую очередь, про ffmpeg, а не про как именно получить прямую ссылку на видео/аудио. Соответственно писать «Это (и многое другое) умеет youtube-dl.» не уточняя о чём речь — крайне некорректно.
          Да, про то что по ссылке всё сводится к ffmpeg я сразу написал, зачем было это повторять?


          1. Zverik
            18.03.2018 16:51

            Если бы в подводке к кату было написано не «как загружать часть видео YouTube при помощи ffmpeg», а «как загружать видео с ютуба и как обрезать его при помощи ffmpeg», вопросов бы не было. Я прочитал статью только потому что подумал: «ого, ffmpeg уже и этому научился?»


        1. SlavikMIPT Автор
          18.03.2018 15:22

          ну не претендую на «целую статью на хабре»(который скатился и вынуждает уже не тратить время на «целые статьи»), но совет как я считаю довольно полезный будет тем, кто занимается проектами с видео/аудио.
          Youtube-dl штука полезная, но очень медленная в большинстве случаев и недостаточно гибкая — использовать в реальном проекте можно разве что для получения метаданных. Я в своем изначально пользовался pytube — она умеет и качать видео и on_progress callback есть и age_restricted обходит, ну и много чего для работы с youtube из кода — лично мне использовать вызовы консольных приложений без обертки не очень удобно


          1. Zverik
            18.03.2018 16:48

            Не-не-не, статья хорошая и полезная. Я, вот, узнал про библиотеку для питона. Она для немного другого — как вы пишете, всякие колбэки и метаданные, но почему бы и нет.


  1. Sad_Bro
    18.03.2018 12:33

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


    1. SlavikMIPT Автор
      18.03.2018 14:02

      да — можно, более того — можно задерживать аудио относительно видео, прокидывать из потока в поток и тд


      1. Sad_Bro
        18.03.2018 14:31

        Пока к сожалению, не получается создать экземпляр с подобным стримом

        >>> yt = YouTube('https://www.youtube.com/watch?v=pJvDIO5P4p0') 
        
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
          File "C:\Python27\lib\site-packages\pytube\__main__.py", line 84, in __init__
            self.prefetch_init()
          File "C:\Python27\lib\site-packages\pytube\__main__.py", line 93, in prefetch_
        init
            self.init()
          File "C:\Python27\lib\site-packages\pytube\__main__.py", line 128, in init
            mixins.apply_signature(self.player_config_args, fmt, self.js)
          File "C:\Python27\lib\site-packages\pytube\mixins.py", line 31, in apply_signa
        ture
            url = stream['url']