Трубопровод с заменой процесса и повторное соединение с выходом

524
user415275

Я пытаюсь использовать мощный удаленный сервер с точки зрения кодирования видео.

У меня есть локальный DVD-привод для копирования DVD в память, и, наконец, в Mbuffer.

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

Часть этого может быть решена с teeпомощью содержимого mbuffer и соответствующего перенаправления:

(reading commands) | mbuffer -p 1 -m 5G | tee <(ffmpeg -i - (splitting video stream here) -f avi | ssh 1.2.3.4 'ffmpeg -i - (doing some encoding here) -f <format> - ') | <( ffmpeg -i (processing audio adequately) )

но это оставляет меня с двумя перенаправленными каналами без логического разделения. Понятно: как мне снова объединить оба потока (мне нужно получить разные входные потоки для другой команды ffmpeg -i <s -tream1> -i <stream2> (doing final conversion) :? Есть ли шанс сделать это?

3

1 ответ на вопрос

2
Kamil Maciorowski

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

mkfifo pre-audio-pipe pre-video-pipe audio-pipe video-pipe # creating pipes (reading commands) | mbuffer -p 1 -m 5G | tee pre-audio-pipe > pre-video-pipe # splitting 

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

«В другом месте» находится в другой консоли:

<pre-audio-pipe (isolate audio) | (process audio) > audio-pipe 

и в еще одной консоли:

<pre-video-pipe (isolate video) | (process video) > video-pipe 

Снова эти две команды будут ждать, пока мы не прочитаем некоторые данные из каналов. В финальной консоли:

ffmpeg -i video-pipe -i audio-pipe (doing final conversion) 

Вы можете столкнуться с блокировкой в ​​случае, если последняя команда хочет прочитать один поток впереди другого. Я не знаю, насколько это вероятно. Дополнительные буферы могут быть полезны, чтобы избежать этого. Моей первой попыткой было бы удалить mbuffer(до tee) и вставить два независимых буфера между соответствующими (isolate)и (process).

После того, как все это сделано:

rm pre-audio-pipe pre-video-pipe audio-pipe video-pipe # cleaning 

редактировать

Из комментария ОП:

Вы видите какой-либо шанс реализовать решение без использования отдельных именованных каналов?

Я думал о сопроцессах ( coprocвстроенных), но я их мало знаю. Есть исчерпывающий ответ о них. Ищите фразу «почему они не так популярны». Оттуда:

Единственное преимущество использования coprocзаключается в том, что вам не нужно очищать эти именованные каналы после использования.

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

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

Шутки в сторону? Со всеми этими (doing some encoding here)расширенными? По моему мнению, независимо от того, используете ли вы именованные или безымянные каналы, «экономическим усилием» здесь будет написание сценария, даже если это одноразовая работа. Сравнивая длинный однострочный и эквивалентный хорошо написанный скрипт, я считаю, что последний легче отлаживать.

Но так как вы попросили однострочник, вы получите его, но с именованными каналами. Моя идея поддерживать именованные каналы - создать для них временный каталог. Общая концепция:

my_temp=`mktemp -d` ; pre_audio_pipe="$/pre-audio-pipe" ; pre_video_pipe="$/pre-video-pipe" ; audio_pipe="$/audio-pipe" ; video_pipe="$/video-pipe" ; mkfifo "$pre_audio_pipe" "$pre_video_pipe" "$audio_pipe" "$video_pipe" ; (reading commands) | tee "$pre_audio_pipe" > "$pre_video_pipe" & <"$pre_audio_pipe" (isolate audio) | mbuffer -p 1 -m 1G | (process audio) > "$audio_pipe" & <"$pre_video_pipe" (isolate video) | mbuffer -p 1 -m 4G | (process video) > "$video_pipe" & ffmpeg -i "$video_pipe" -i "$audio_pipe" (doing final conversion) ; rm -rf "$my_temp" 

В соответствии с этим ответом вы, вероятно, можете поместить его в одну командную строку, даже после того, как вы углубитесь в команду и развернете все эти (do something)заполнители.

Хорошо, форма с одной строчкой должна была показать вам, насколько это неудобно. Та же концепция, что и сценарий:

#!/bin/bash  my_temp=`mktemp -d` pre_audio_pipe="$/pre-audio-pipe" pre_video_pipe="$/pre-video-pipe" audio_pipe="$/audio-pipe" video_pipe="$/video-pipe"  mkfifo "$pre_audio_pipe" "$pre_video_pipe" "$audio_pipe" "$video_pipe" #creating actual pipes  # Main code here. # Notice we put few commands into the background. # In this example there are two separate mbuffers. (reading commands) | tee "$pre_audio_pipe" > "$pre_video_pipe" & # splitting <"$pre_audio_pipe" (isolate audio) | mbuffer -p 1 -m 1G | (process audio) > "$audio_pipe" & <"$pre_video_pipe" (isolate video) | mbuffer -p 1 -m 4G | (process video) > "$video_pipe" & ffmpeg -i "$video_pipe" -i "$audio_pipe" (doing final conversion)  # Then cleaning: rm -rf "$my_temp" 
Интересно, это похоже (или идентично) именованию файловых дескрипторов? Xen2050 7 лет назад 0
@ Xen2050 Честно говоря, я не знаю. Мой явный опыт работы с файловыми дескрипторами (в `bash` или в другом месте) очень ограничен. Конечно, я использую их неявно все время. :) Безымянные каналы (`|`) являются эфемерными FIFO; именованные каналы - это более постоянные FIFO с именами (путями). На данный момент мне кажется, что самое большее может быть сходство, а не идентичность. Kamil Maciorowski 7 лет назад 0
Спасибо за ответ на мой вопрос. Я понимаю ваш подход; Вы видите какой-либо шанс реализовать решение без использования отдельных именованных каналов? В идеале, это была бы однострочная команда, работающая только с неназванными каналами, поскольку задание должно быть запущено с «экономическим усилием» из командной строки. user415275 7 лет назад 0
@ user415275 Я расширил свой ответ. Kamil Maciorowski 7 лет назад 0
Большое спасибо за ответ. Ваш большой «однострочник» все равно использует fifo / именованные каналы - есть ли шанс достичь следующего: - чтение входной даты только один раз - переход отсюда, обработка всех данных - и: последующее присоединение к полученным данным опять без именных каналов? Я абсолютно ценю вашу работу и вашу самоотверженность, и, конечно, она работает, однако это не тот прагматичный способ, которым я бы хотел выбрать, если бы это было возможно. user415275 7 лет назад 0
@ user415275 Как я уже сказал: сопроцессы; но я не собираюсь включать их в свой код здесь, потому что я недостаточно знаком с ними, и я думаю, что у вас уже есть все средства, чтобы сделать это самостоятельно, поскольку вы настаиваете на том, чтобы отказаться от именованных каналов. Вы можете оставить свой ответ и позволить другим учиться у вас. В случае, если есть определенная проблема с сопроцессами, которую вы не можете решить, несмотря на разумные усилия по исследованию, я призываю вас задать отдельный вопрос о них явно (для суперпользователя или [Unix & Linux] (http://unix.stackexchange.com/) ). Kamil Maciorowski 7 лет назад 0