Fofer

Multichannel-View

Jul 4th, 2025 (edited)
143
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.43 KB | None | 0 0
  1. from flask import Flask, Response, request, stream_with_context
  2. import subprocess
  3. import time
  4.  
  5. app = Flask(__name__)
  6.  
  7. # === Configuration ===
  8. CDVR_HOST = '192.168.0.13'        # IP address of the Channels DVR server
  9. MULTICHANNEL_VIEW_PORT = 5001  # Port for running the Flask app
  10. STARTUP_DELAY = 3              # Delay before FFmpeg starts, in seconds (adjust if streams are slow)
  11. LABEL_FONT_SIZE = 36           # Font size for on-screen labels
  12. LABEL_FADE_IN = 1              # seconds to fade in
  13. LABEL_DISPLAY = 10             # how long label is fully visible
  14. LABEL_FADE_OUT = 2             # seconds to fade out
  15.  
  16. # === Channel Number → Name Legend (editable by users) ===
  17. CHANNEL_NAME_RAW = """
  18. 5.1=KTLA 5
  19. 9.1=KCAL 9
  20. 1000=CBS 2
  21. 1002=FOX 11
  22. 1003=KCAL 9
  23. 1084=FOX News
  24. 1141=MSNBC
  25. 1160=NewsNation
  26. 2137=FOX 11
  27. 5225=CNN
  28. 6985=ABC News
  29. """
  30.  
  31. def parse_channel_name_map(raw_text):
  32.     mapping = {}
  33.     for line in raw_text.strip().splitlines():
  34.         if '=' in line:
  35.             ch_num, ch_name = line.strip().split('=', 1)
  36.             mapping[ch_num.strip()] = ch_name.strip()
  37.     return mapping
  38.  
  39. CHANNEL_NAME_MAP = parse_channel_name_map(CHANNEL_NAME_RAW)
  40.  
  41. def build_input_urls(channels):
  42.     return [f"http://{CDVR_HOST}:8089/devices/ANY/channels/{ch}/stream.mpg" for ch in channels]
  43.  
  44. @app.route('/combine')
  45. def combine_streams():
  46.     channels = request.args.getlist('ch')[:4]
  47.     if not channels:
  48.         return "No channels provided", 400
  49.  
  50.     urls = build_input_urls(channels)
  51.     num_inputs = len(urls)
  52.  
  53.     ffmpeg_cmd = ['ffmpeg', '-hide_banner', '-loglevel', 'error']
  54.  
  55.     for url in urls:
  56.         ffmpeg_cmd += ['-ss', '1', '-i', url]
  57.  
  58.     filter_parts = []
  59.     for i, ch in enumerate(channels):
  60.         label = CHANNEL_NAME_MAP.get(ch, f"CH {ch}")
  61.         filter_parts.append(
  62.             f"[{i}:v]scale=1280:720,fps=30000/1001,"
  63.             f"drawtext=text='{label}':x=10:y=10:fontsize={LABEL_FONT_SIZE}:"
  64.             f"fontcolor=white:box=1:[email protected]:"
  65.             f"alpha='if(lt(t,{LABEL_FADE_IN}),t/{LABEL_FADE_IN},"
  66.             f"if(lt(t,{LABEL_FADE_IN}+{LABEL_DISPLAY}),1,"
  67.             f"if(lt(t,{LABEL_FADE_IN}+{LABEL_DISPLAY}+{LABEL_FADE_OUT}),"
  68.             f"1-(t-{LABEL_FADE_IN}-{LABEL_DISPLAY})/{LABEL_FADE_OUT},0)))'[v{i}]"
  69.         )
  70.         ffmpeg_cmd += ['-map', f'{i}:a?']  # Optional audio mapping
  71.  
  72.     layout_map = {
  73.         1: "[v0]xstack=inputs=1:layout=0_0[v]",
  74.         2: "[v0][v1]xstack=inputs=2:layout=0_0|w0_0[v]",
  75.         3: "[v0][v1][v2]xstack=inputs=3:layout=0_0|w0_0|0_h0[v]",
  76.         4: "[v0][v1][v2][v3]xstack=inputs=4:layout=0_0|w0_0|0_h0|w0_h0[v]"
  77.     }
  78.     filter_parts.append(layout_map[num_inputs])
  79.     filter_complex = ';'.join(filter_parts)
  80.  
  81.     ffmpeg_cmd += [
  82.         '-filter_complex', filter_complex,
  83.         '-map', '[v]',
  84.         '-c:v', 'h264_videotoolbox',
  85.         '-b:v', '5120k',
  86.         '-f', 'mpegts',
  87.         'pipe:1'
  88.     ]
  89.  
  90.     def generate():
  91.         time.sleep(STARTUP_DELAY)
  92.         process = subprocess.Popen(ffmpeg_cmd, stdout=subprocess.PIPE)
  93.         try:
  94.             while True:
  95.                 chunk = process.stdout.read(1024 * 16)
  96.                 if not chunk:
  97.                     break
  98.                 yield chunk
  99.         finally:
  100.             process.kill()
  101.  
  102.     return Response(stream_with_context(generate()), mimetype='video/MP2T')
  103.  
  104. if __name__ == '__main__':
  105.     app.run(host='0.0.0.0', port=MULTICHANNEL_VIEW_PORT, debug=True)
Advertisement
Add Comment
Please, Sign In to add comment