first commit

master
Mustafa Yontar 3 years ago
commit 6b48bd52b5
  1. 10
      .gitignore
  2. 8
      .idea/.gitignore
  3. 26
      .idea/inspectionProfiles/Project_Default.xml
  4. 6
      .idea/inspectionProfiles/profiles_settings.xml
  5. 4
      .idea/misc.xml
  6. 8
      .idea/modules.xml
  7. 10
      .idea/tryCanvas.iml
  8. 31
      StreamMerger.py
  9. 43
      VideoFile.py
  10. BIN
      __pycache__/StreamMerger.cpython-38.pyc
  11. BIN
      __pycache__/VideoFile.cpython-38.pyc
  12. BIN
      antonio.ttf
  13. 41
      main.py
  14. 3
      requirements.txt
  15. 37
      stream.sh
  16. 46
      testsup.py

10
.gitignore vendored

@ -0,0 +1,10 @@
*.webm
*.mp4
Dash/*
chunk/*
.venv
*.flv
*.jpg
*.mpd
*.m4s
*.pyd

8
.idea/.gitignore vendored

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

@ -0,0 +1,26 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<Languages>
<language minSize="62" name="Python" />
</Languages>
</inspection_tool>
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="python-snap7" />
</list>
</value>
</option>
</inspection_tool>
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<option name="ignoredErrors">
<list>
<option value="N806" />
</list>
</option>
</inspection_tool>
</profile>
</component>

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8 (tryCanvas)" project-jdk-type="Python SDK" />
</project>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/tryCanvas.iml" filepath="$PROJECT_DIR$/.idea/tryCanvas.iml" />
</modules>
</component>
</project>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.8 (tryCanvas)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

@ -0,0 +1,31 @@
import time
from datetime import datetime
from threading import Thread
from PIL import Image
from PIL import ImageDraw, ImageFont
from VideoFile import VideoFile
class StreamMerger(Thread):
img = None
def run(self) -> None:
video = VideoFile("yiv3.webm")
video2 = VideoFile("supernatural.webm")
video.open()
video2.open()
start_time = time.time()
while True:
img = Image.new('RGB', (1920, 1080), color='black')
if video.frame is not None:
img.paste(video.frame)
if video2.frame is not None:
img.paste(video2.frame, (0, 600))
fnt = ImageFont.truetype("antonio.ttf", 40)
d = ImageDraw.Draw(img)
d.text((0, 1000), str(int(time.time() - start_time)), font=fnt)
self.img = img
time.sleep(1.0 / 1000.0)

@ -0,0 +1,43 @@
import time
from threading import Thread
import av
from PIL import Image
class VideoFile(Thread):
file: str = ""
frame: Image = None
running: bool = False
def __init__(self, file: str):
self.file = file
super().__init__()
def get_frame(self):
return self.frame
def stop(self):
self.running = False
def open(self):
self.running = True
self.start()
def run(self) -> None:
container = av.open(self.file)
video = container.streams.video[0]
print(video.time_base)
while self.running:
try:
for frame in container.decode(video):
if self.running:
self.frame = frame.to_image()
else:
break
time.sleep(1.0 / 60)
except:
self.running = False

Binary file not shown.

@ -0,0 +1,41 @@
import time
from datetime import datetime
import av
from PIL import Image
from av.frame import Fraction
from PIL import Image, ImageDraw, ImageFont
from StreamMerger import StreamMerger
from VideoFile import VideoFile
container = av.open('rtmp://localhost/live', mode='w', format='flv')
stream = container.add_stream('h264', framerate=60) # alibi frame rate
output_audio_stream = container.add_stream('aac', rate=44100)
stream.width = 1920
stream.height = 1080
stream.pix_fmt = 'yuv420p'
# stream.bit_rate = 128000
stream.time_base = Fraction(1, 60)
st = StreamMerger()
st.start()
my_pts = 0 # [seconds]
frame_i = 0
time_start = time.time()
while True:
if st.img is not None:
frame = av.VideoFrame.from_image(st.img)
framen = frame.reformat(format='yuv420p')
ntime = time.time() - time_start
frame.pts = ntime * 60
frame.time_base = Fraction(1, 60)
# frame.key_frame = frame_i
frame_i += 1
# print(dir(frame))
for packet in stream.encode(framen):
container.mux(packet)
time.sleep(1.0 / 60.0)

@ -0,0 +1,3 @@
av
pillow
aiortc

@ -0,0 +1,37 @@
#!/bin/bash
#HLS, Dash and fallback code from zazu.berlin 2020, Version 20200424
VIDEO_IN=rtmp://localhost/live
VIDEO_OUT=master
HLS_TIME=4
FPS=25
GOP_SIZE=100
PRESET_P=veryslow
V_SIZE_1=960x540
V_SIZE_2=416x234
V_SIZE_3=640x360
V_SIZE_4=768x432
V_SIZE_5=1280x720
V_SIZE_6=1920x1080
# DASH
ffmpeg -i $VIDEO_IN -y \
-preset $PRESET_P -keyint_min $GOP_SIZE -g $GOP_SIZE -sc_threshold 0 -r $FPS -c:v libx264 -pix_fmt yuv420p \
-map v:0 -s:0 $V_SIZE_1 -b:v:0 2M -maxrate:0 2.14M -bufsize:0 3.5M \
-map v:0 -s:1 $V_SIZE_2 -b:v:1 145k -maxrate:1 155k -bufsize:1 220k \
-map v:0 -s:2 $V_SIZE_3 -b:v:2 365k -maxrate:2 390k -bufsize:2 640k \
-map v:0 -s:3 $V_SIZE_4 -b:v:3 730k -maxrate:3 781k -bufsize:3 1278k \
-map v:0 -s:4 $V_SIZE_4 -b:v:4 1.1M -maxrate:4 1.17M -bufsize:4 2M \
-map v:0 -s:5 $V_SIZE_5 -b:v:5 3M -maxrate:5 3.21M -bufsize:5 5.5M \
-map v:0 -s:6 $V_SIZE_5 -b:v:6 4.5M -maxrate:6 4.8M -bufsize:6 8M \
-map v:0 -s:7 $V_SIZE_6 -b:v:7 6M -maxrate:7 6.42M -bufsize:7 11M \
-map v:0 -s:8 $V_SIZE_6 -b:v:8 7.8M -maxrate:8 8.3M -bufsize:8 14M \
-init_seg_name init\$RepresentationID\$.\$ext\$ -media_seg_name chunk\$RepresentationID\$-\$Number%05d\$.\$ext\$ \
-use_template 1 -use_timeline 1 \
-seg_duration 4 -adaptation_sets "id=0,streams=v id=1,streams=a" \
-f dash Dash/dash.mpd
# Fallback video file
ffmpeg -i $VIDEO_IN -y -c:v libx264 -pix_fmt yuv420p -r $FPS -s $V_SIZE_1 -b:v 1.8M -c:a aac -b:a 128k -ac 1 -ar 44100 fallback-video-$V_SIZE_1.mp4

@ -0,0 +1,46 @@
import av
from av.filter import Graph
def main():
input_container = av.open('supernatural.webm', mode='r')
input_container2 = av.open('yiv3.webm', mode='r')
input_video_stream = input_container.streams.video[0]
output_container = av.open("out.mp4", "w")
output_audio_stream = output_container.add_stream('aac', rate=input_container2.streams.audio[0].base_rate)
graph = Graph()
in_src = graph.add_abuffer(template=input_container.streams.audio[0])
in_src2 = graph.add_abuffer(template=input_container2.streams.audio[0])
pad = graph.add("amix", "inputs=2:duration=longest")
print(dir(pad))
in_src.link_to(pad, 0, 0)
in_src2.link_to(pad, 0, 1)
sink = graph.add('abuffersink')
pad.link_to(sink)
graph.configure()
for frame in input_container.decode(audio=0):
in_src.push(frame)
for frame in input_container2.decode(audio=0):
in_src2.push(frame)
while True:
try:
filtered_frame = pad.pull()
output_container.mux(output_audio_stream.encode(filtered_frame))
except:
break
for packet in output_audio_stream.encode():
output_container.mux(packet)
input_container.close()
input_container2.close()
output_container.close()
if __name__ == '__main__':
main()
Loading…
Cancel
Save