Hey again.
Just letting you know that Iâve found a solution to my issue. Iâve succeed with uploading a video to Streamlit Cloud, processing it with OpenCV functions (and with DL models as next steps), rendering it to a video file using h264 codec and displaying it at my Streamlit web page.
Iâve managed to do that using the tempfile library to create a temp file where I put a rendered video. Use it and the issue with FileNotFoundError
should be gone. But this was not the main problem.
The main problem was to render a video with h264 codec to display it in Streamlitâs html5 web player. The point is that OpenCV doesnât support h264 from scratch, you should either manually build it (I canât even imagine how to do that for Streamlit Cloud) or just put the openh264-1.8.0-win64.dll file to your project folder (this is what I did to make my script work locally, I assume that it was your approach as well, otherwise cv.VideoWriter_fourcc(*âavc1â) just wonât work).
The openh264-1.8.0-win64.dll file obviously compiled for Windows, so such approach of course doesnât work at Streamlit Cloud. Maybe itâs possible to find a compiled h264 codec for Streamlit OS, but first I am not even sure what version of Linux they use and second I donât know if OpenCV will be able to implicitly attach this codec in Streamlit Cloud file system. So I gave up this idea anyway.
Instead I used the moviepy library to render a video in h264. Itâs a wrapper over ffmpeg and It provides pretty straight-forward and easy functions to cut, edit and render video clips. To save video at Streamlit Cloud I need to save all numpy video frames to memory and then render it to drive, itâs not a very optimized approach. But it works for now, maybe I will improve it in future. Here goes the code of my app, something like that should work for you.
import streamlit as st
import cv2
import tempfile
import moviepy.editor as mpy
video_data = st.file_uploader("Upload file", ['mp4','mov', 'avi'])
if video_data:
# save uploaded video to disc
temp_file_1 = tempfile.NamedTemporaryFile(delete=False,suffix='.mp4')
temp_file_1.write(video_data.getbuffer())
# read it with cv2.VideoCapture(), so now we can process it with OpenCV functions
cap = cv2.VideoCapture(temp_file_1.name)
# grab some parameters of video to use them for writing a new, processed video
frame_fps = int(cap.get(cv2.CAP_PROP_FPS))
# specify a writer to write a processed video to a disk frame by frame
temp_file_2 = tempfile.NamedTemporaryFile(delete=False,suffix='.mp4')
# loop though a video, process each frame and save it to a list
video_row=[]
while True:
ret, frame = cap.read()
# if frame is read correctly ret is True
if not ret:
st.write("Video is ready! Now you can play it")
break
# some video processing here
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# write a processed frame to the list
gray = cv2.merge((gray,gray,gray))
video_row.append(gray)
## Close video file
cap.release()
# create moviepy object from list with numpy frames and save it to a disk
clip = mpy.ImageSequenceClip(video_row, fps=25)
clip.write_videofile(temp_file_2.name)
# when video is fully saved to disk, open it as BytesIO and play with st.video()
# result_video = open(temp_file_result, "rb")
st.video(temp_file_2.name)
result_video = open(temp_file_2.name, "rb")
st.download_button(label="Download video file", data=result_video,file_name='video_clip.mp4')
Just in case if you still want to render your video precisely with OpenCV cv.VideoWriter method you will need to find a way to deploy it to Streamlit Cloud with h264. You may render your video with âmp4vâ codec instead, in that case you wonât be able to display it with st.video() method. But you still will be able to download this rendered video file with st.download_button.
I hope that helps with your issue. Good luck.