Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- from flask import Flask, Response, request, stream_with_context
- import subprocess
- import time
- app = Flask(__name__)
- # === Configuration ===
- CDVR_HOST = '192.168.0.13' # IP address of the Channels DVR server
- MULTICHANNEL_VIEW_PORT = 5001 # Port for running the Flask app
- STARTUP_DELAY = 3 # Delay before FFmpeg starts, in seconds (adjust if streams are slow)
- LABEL_FONT_SIZE = 36 # Font size for on-screen labels
- LABEL_FADE_IN = 1 # seconds to fade in
- LABEL_DISPLAY = 10 # how long label is fully visible
- LABEL_FADE_OUT = 2 # seconds to fade out
- # === Channel Number → Name Legend (editable by users) ===
- CHANNEL_NAME_RAW = """
- 5.1=KTLA 5
- 9.1=KCAL 9
- 1000=CBS 2
- 1002=FOX 11
- 1003=KCAL 9
- 1084=FOX News
- 1141=MSNBC
- 1160=NewsNation
- 2137=FOX 11
- 5225=CNN
- 6985=ABC News
- """
- def parse_channel_name_map(raw_text):
- mapping = {}
- for line in raw_text.strip().splitlines():
- if '=' in line:
- ch_num, ch_name = line.strip().split('=', 1)
- mapping[ch_num.strip()] = ch_name.strip()
- return mapping
- CHANNEL_NAME_MAP = parse_channel_name_map(CHANNEL_NAME_RAW)
- def build_input_urls(channels):
- return [f"http://{CDVR_HOST}:8089/devices/ANY/channels/{ch}/stream.mpg" for ch in channels]
- @app.route('/combine')
- def combine_streams():
- channels = request.args.getlist('ch')[:4]
- if not channels:
- return "No channels provided", 400
- urls = build_input_urls(channels)
- num_inputs = len(urls)
- ffmpeg_cmd = ['ffmpeg', '-hide_banner', '-loglevel', 'error']
- for url in urls:
- ffmpeg_cmd += ['-ss', '1', '-i', url]
- filter_parts = []
- for i, ch in enumerate(channels):
- label = CHANNEL_NAME_MAP.get(ch, f"CH {ch}")
- filter_parts.append(
- f"[{i}:v]scale=1280:720,fps=30000/1001,"
- f"drawtext=text='{label}':x=10:y=10:fontsize={LABEL_FONT_SIZE}:"
- f"fontcolor=white:box=1:[email protected]:"
- f"alpha='if(lt(t,{LABEL_FADE_IN}),t/{LABEL_FADE_IN},"
- f"if(lt(t,{LABEL_FADE_IN}+{LABEL_DISPLAY}),1,"
- f"if(lt(t,{LABEL_FADE_IN}+{LABEL_DISPLAY}+{LABEL_FADE_OUT}),"
- f"1-(t-{LABEL_FADE_IN}-{LABEL_DISPLAY})/{LABEL_FADE_OUT},0)))'[v{i}]"
- )
- ffmpeg_cmd += ['-map', f'{i}:a?'] # Optional audio mapping
- layout_map = {
- 1: "[v0]xstack=inputs=1:layout=0_0[v]",
- 2: "[v0][v1]xstack=inputs=2:layout=0_0|w0_0[v]",
- 3: "[v0][v1][v2]xstack=inputs=3:layout=0_0|w0_0|0_h0[v]",
- 4: "[v0][v1][v2][v3]xstack=inputs=4:layout=0_0|w0_0|0_h0|w0_h0[v]"
- }
- filter_parts.append(layout_map[num_inputs])
- filter_complex = ';'.join(filter_parts)
- ffmpeg_cmd += [
- '-filter_complex', filter_complex,
- '-map', '[v]',
- '-c:v', 'h264_videotoolbox',
- '-b:v', '5120k',
- '-f', 'mpegts',
- 'pipe:1'
- ]
- def generate():
- time.sleep(STARTUP_DELAY)
- process = subprocess.Popen(ffmpeg_cmd, stdout=subprocess.PIPE)
- try:
- while True:
- chunk = process.stdout.read(1024 * 16)
- if not chunk:
- break
- yield chunk
- finally:
- process.kill()
- return Response(stream_with_context(generate()), mimetype='video/MP2T')
- if __name__ == '__main__':
- app.run(host='0.0.0.0', port=MULTICHANNEL_VIEW_PORT, debug=True)
Advertisement
Add Comment
Please, Sign In to add comment