diff --git a/camera_stream_recording/Dockerfile b/camera_stream_recording/Dockerfile new file mode 100644 index 0000000..53bce46 --- /dev/null +++ b/camera_stream_recording/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3.7 + +COPY requirements.txt requirements.txt +RUN pip3 install -r requirements.txt + +COPY . . + +CMD [ "python","-u", "./async_wrapper.py" ] diff --git a/camera_stream_recording/README.md b/camera_stream_recording/README.md index 9eb723a..c9d6984 100644 --- a/camera_stream_recording/README.md +++ b/camera_stream_recording/README.md @@ -1,3 +1,22 @@ +## deploy 24/7 recording to aks +To have a 24/7 recording running in kubernetes and storing the results to azure blob follow these steps +````bash +az login +az acr login --name fkkstudents +```` +make sure you are in the ./camera_stream_recording/src folder +````bash +docker build -t fkkstudents.azurecr.io/recording/camera_recorder . +docker push fkkstudents.azurecr.io/recording/camera_recorder +```` + +get .env file with the correct credentials (a backup is stored in the SaveNow storage account fkk247/credentials) + +````bash +az aks get-credentials --resource-group fkkstudents --name fkkstudents +kubectl apply -f deployament.yaml +```` + ## Capture Videostream This tool has two basic mode. At first you can generate training data for manual labeling in cvat [here is the relevant fork](https://github.com/jul095/cvat). diff --git a/camera_stream_recording/Utils.py b/camera_stream_recording/Utils.py index 4dd53a6..bfef0b9 100644 --- a/camera_stream_recording/Utils.py +++ b/camera_stream_recording/Utils.py @@ -57,24 +57,26 @@ def is_video_consistent(filename, video_duration): :return: if the video is consistent due to a threshold :rtype: bool """ - video_length = get_length(filename) - # Check if video is 8 percent shorter than expected - # check if the video is shorter than the expected length. This could be a "jump" in the video -> delete video - expected_length = video_duration - tolerance = expected_length - (expected_length * 0.1) - if video_length < tolerance: - print('video is 1 percent shorter than expected:') - print('video_length: ' + str(video_length)) - print('expected_length: ' + str(expected_length)) - print('tolerance: ' + str(tolerance)) - return False - else: - print('video_length: ' + str(video_length)) - print('expected_length: ' + str(expected_length)) + # This is causing weird "file not found" issues --> uncommenting for now. MJ 21.03.2021 + + # video_length = get_length(filename) + # # Check if video is 8 percent shorter than expected + # # check if the video is shorter than the expected length. This could be a "jump" in the video -> delete video + # expected_length = video_duration + # tolerance = expected_length - (expected_length * 0.1) + # if video_length < tolerance: + # print('video is 1 percent shorter than expected:') + # print('video_length: ' + str(video_length)) + # print('expected_length: ' + str(expected_length)) + # print('tolerance: ' + str(tolerance)) + # return False + # else: + # print('video_length: ' + str(video_length)) + # print('expected_length: ' + str(expected_length)) return True -def upload_video(filename, uploads_list, storage_addr, sas_token): +def upload_video(filename, SAS_TOKEN, STORAGE_ACCOUNT, STORAGE_CONTAINER): """ Prepare the upload of files to a azure Filestorage @@ -89,16 +91,15 @@ def upload_video(filename, uploads_list, storage_addr, sas_token): :return: - :rtype: void """ - print("Upload file " + filename) - dest = storage_addr + filename + sas_token - - sub_p_id = subprocess.Popen(["./azcopy", "copy", filename, dest], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - uploads_list.append([sub_p_id, filename]) + print("Uploading file %s to %s / %s " % (filename, STORAGE_ACCOUNT, STORAGE_CONTAINER)) + block_blob_service = BlockBlobService(account_name=STORAGE_ACCOUNT, sas_token=SAS_TOKEN) + block_blob_service.create_blob_from_path(STORAGE_CONTAINER, filename, filename) + print("Upload successfull. Removing local file %s" % filename) + os.remove(filename) return + def get_end_timestamp_from_minutes_duration(video_max_duration): return datetime.now() + timedelta(minutes=video_max_duration) diff --git a/camera_stream_recording/async_wrapper.py b/camera_stream_recording/async_wrapper.py new file mode 100644 index 0000000..0b4930a --- /dev/null +++ b/camera_stream_recording/async_wrapper.py @@ -0,0 +1,6 @@ +import time +from subprocess import Popen +while True: + print("starting new process with video_recording_main.py") + p = Popen('python main.py -u -p -vd 10', shell=True) + time.sleep(600) diff --git a/camera_stream_recording/deployment.yaml b/camera_stream_recording/deployment.yaml new file mode 100644 index 0000000..93f62bd --- /dev/null +++ b/camera_stream_recording/deployment.yaml @@ -0,0 +1,27 @@ +kind: Namespace +apiVersion: v1 +metadata: + name: camera-recorder + labels: + name: camera-recorder +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: camera-recorder-deployment + namespace: camera-recorder + labels: + app: camera-recorder +spec: + replicas: 1 + selector: + matchLabels: + app: camera-recorder + template: + metadata: + labels: + app: camera-recorder + spec: + containers: + - name: camera-recorder + image: fkkstudents.azurecr.io/recording/camera_recorder:latest diff --git a/camera_stream_recording/env.example b/camera_stream_recording/env.example index 433c72c..a2462e3 100644 --- a/camera_stream_recording/env.example +++ b/camera_stream_recording/env.example @@ -2,4 +2,5 @@ CAMERA_IP_ADDR= CAMERA_USER_NAME= PASSWORD= SAS_TOKEN= -STORAGE_ADDR= \ No newline at end of file +STORAGE_ACCOUNT= +STORAGE_CONTAINER= \ No newline at end of file diff --git a/camera_stream_recording/main.py b/camera_stream_recording/main.py index 3817170..998f701 100644 --- a/camera_stream_recording/main.py +++ b/camera_stream_recording/main.py @@ -37,7 +37,6 @@ def __init__(self, username, password, ip_addr, is_uploaded): self.fourcc = cv2.VideoWriter_fourcc('M', 'J', 'P', 'G') self.link = f"rtsp://{username}:{password}@{ip_addr}:80/axis-media/media.amp?streamprofile=rtspStreamLow" self.cap = cv2.VideoCapture(self.link) - print(self.link) self.uploads = [] self.is_uploaded = is_uploaded if not self.cap.isOpened(): @@ -45,16 +44,17 @@ def __init__(self, username, password, ip_addr, is_uploaded): def start_capture_one_video(self): """Initialize the Video Writer""" - vid_name = generate_unique_name() + ".mp4" - out = cv2.VideoWriter(vid_name, self.fourcc, 30.0, - (1920, 1080)) + vid_name = generate_unique_name() + ".avi" + fourcc = cv2.VideoWriter_fourcc(*'MPEG') + out = cv2.VideoWriter(vid_name, fourcc, 30.0, + (1920, 1080)) return vid_name, out def handle_video_saving(self, filename, sub_procs_list, video_duration): """Method will start uploading videos and delete the files if there are not consistent""" if is_video_consistent(filename, video_duration): if self.is_uploaded: - upload_video(filename, sub_procs_list, config('STORAGE_ADDR'), config('SAS_TOKEN')) + upload_video(filename, config('SAS_TOKEN'), config('STORAGE_ACCOUNT'),config('STORAGE_CONTAINER') ) else: print("No upload, because the script runs locally\n") else: @@ -66,7 +66,7 @@ def handle_video_saving_specific_time(self, filename, sub_procs_list, """Method will start uploading and keep the files if there are not consistent""" if is_video_consistent(filename, video_duration): if self.is_uploaded: - upload_video(filename, sub_procs_list, config('STORAGE_ADDR'), config('SAS_TOKEN')) + upload_video(filename, config('SAS_TOKEN'), config('STORAGE_ACCOUNT'),config('STORAGE_CONTAINER') ) else: print("No upload, because the script runs locally\n") else: @@ -130,7 +130,7 @@ def capture_video_stream_for_specific_time(self, end_timestamp=None): cam_connect = self.cap.read() time.sleep(0.5) - cv2.namedWindow("CameraView", cv2.WINDOW_NORMAL) + # cv2.namedWindow("CameraView", cv2.WINDOW_NORMAL) while True: prev_stream_frame = stream_frame try: @@ -164,7 +164,7 @@ def capture_video_stream_for_specific_time(self, end_timestamp=None): vis_frame = Display("Connect: " + str(connect_count), vis_frame, (5, 80)).overlay_text() if cam_connect: - cv2.imshow("CameraView", vis_frame) + # cv2.imshow("CameraView", vis_frame) self.out.write(stream_frame) if end_timestamp is not None: @@ -187,7 +187,7 @@ def capture_video_stream_for_specific_time(self, end_timestamp=None): self.handle_video_saving_specific_time(self.vid_name, self.uploads, expected_length) self.check_for_completed_uploads(self.uploads, None) - cv2.destroyAllWindows() + # cv2.destroyAllWindows() # self.match_with_first_timestamp() def match_with_first_timestamp(self): @@ -299,6 +299,10 @@ def capture_images_every_minute(self, runtime_duration): if datetime.datetime.now() >= end_time_script: break +def entry_for_async_recording(): + video_capturing = MainCaptureHandling(config('CAMERA_USER_NAME'), + config('PASSWORD'), config('CAMERA_IP_ADDR'), True) + video_capturing.capture_video_stream_for_specific_time(get_end_timestamp_from_minutes_duration(1)) if __name__ == "__main__": @@ -352,6 +356,7 @@ def capture_images_every_minute(self, runtime_duration): # Run only one planned capturing of one video user defined if args.video_duration: vdm_duration = args.video_duration + print("running planned video capturing for a specific time") video_capturing.capture_video_stream_for_specific_time( get_end_timestamp_from_minutes_duration(vdm_duration)) else: diff --git a/camera_stream_recording/requirements.txt b/camera_stream_recording/requirements.txt index 8b9cd1e..68056c2 100644 --- a/camera_stream_recording/requirements.txt +++ b/camera_stream_recording/requirements.txt @@ -3,3 +3,4 @@ opencv-python==4.3.0.36 python-dateutil==2.8.1 python-decouple==3.4 six==1.15.0 +azure-storage-blob==2.1.0 \ No newline at end of file