commit
6b48bd52b5
@ -0,0 +1,10 @@ |
||||
*.webm |
||||
*.mp4 |
||||
Dash/* |
||||
chunk/* |
||||
.venv |
||||
*.flv |
||||
*.jpg |
||||
*.mpd |
||||
*.m4s |
||||
*.pyd |
@ -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.
Binary file not shown.
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…
Reference in new issue