1# !/usr/bin/env python3 2# coding=utf-8 3""" 4* Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd. 5* Licensed under the Apache License, Version 2.0 (the "License"); 6* you may not use this file except in compliance with the License. 7* You may obtain a copy of the License at 8* 9* http://www.apache.org/licenses/LICENSE-2.0 10* 11* Unless required by applicable law or agreed to in writing, software 12* distributed under the License is distributed on an "AS IS" BASIS, 13* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14* See the License for the specific language governing permissions and 15* limitations under the License. 16""" 17 18import struct 19import zlib 20import os 21import argparse 22import re 23import pip 24 25 26def lost_module(module_name): 27 print(""" 28need %s module, try install first: 29 30 pip install %s""" % (module_name, module_name)) 31 exit() 32 33 34try: 35 import cv2 36except ImportError: 37 pip.main(["install", "opencv-python", "-i", "https://pypi.tuna.tsinghua.edu.cn/simple"]) 38 try: 39 import cv2 40 except ImportError: 41 cv2 = None 42 lost_module("opencv-python") 43 44try: 45 from PIL import Image 46except ImportError: 47 pip.main(["install", "pillow"]) 48 try: 49 from PIL import Image 50 except ImportError: 51 Image = None 52 lost_module("pillow") 53 54try: 55 import numpy as np 56except ImportError: 57 pip.main(["install", "numpy"]) 58 try: 59 import numpy as np 60 except ImportError: 61 np = None 62 lost_module("numpy") 63 64 65class RawMaker: 66 """ 67 Make a boot video by a MP4 file or some .img files: 68 """ 69 70 def __init__(self, args): 71 self._mp4 = args.mp4 72 self._image = args.image 73 self._out = args.out 74 self._display = [int(i) for i in re.split(r'[xX* ]+', args.display.strip())] 75 self._rotate = args.rotate 76 self._flip = args.flip 77 self._fnp = 0 78 self._vdo = None 79 self._image_files = [] 80 81 def _iter_img(self): 82 if self._mp4: 83 success, frame = self._vdo.read() 84 if success: 85 image = Image.fromarray(frame) 86 return success, image 87 else: 88 return False, None 89 else: 90 if self._fnp >= len(self._image_files): 91 return False, None 92 image = Image.open(os.path.join(self._image, self._image_files[self._fnp])) 93 self._fnp += 1 94 return True, image 95 96 def make(self): 97 frame_count, width, height = 0, 0, 0 98 if self._mp4: 99 if not os.path.exists(self._mp4): 100 print("mp4 file %s is not exist" % self._mp4) 101 exit() 102 self._vdo = cv2.VideoCapture(self._mp4) 103 fps = int(self._vdo.get(cv2.CAP_PROP_FPS)) 104 w = int(self._vdo.get(cv2.CAP_PROP_FRAME_WIDTH)) 105 h = int(self._vdo.get(cv2.CAP_PROP_FRAME_HEIGHT)) 106 frame_count = int(self._vdo.get(cv2.CAP_PROP_FRAME_COUNT)) 107 if fps != 30: 108 print("video fps :", fps, ", width :", w, ", height :", h, ", frame count :", frame_count) 109 if frame_count <= 0: 110 exit() 111 elif self._image: 112 for fn in os.listdir(self._image): 113 self._image_files.append(fn) 114 frame_count = len(self._image_files) 115 if frame_count <= 0: 116 exit() 117 self._image_files.sort() 118 else: 119 exit() 120 121 output_bytes = bytearray(b"RAW.diff") 122 offset = 8 123 screen_old_bytes = None 124 num = 0 125 while True: 126 ret, img = self._iter_img() 127 if not ret: 128 break 129 num += 1 130 img = img.convert("RGBA") 131 if self._flip: 132 img = img.transpose(Image.FLIP_LEFT_RIGHT) 133 if self._rotate == 90: 134 img = img.transpose(Image.ROTATE_90) 135 elif self._rotate == 180: 136 img = img.transpose(Image.ROTATE_180) 137 elif self._rotate == 270: 138 img = img.transpose(Image.ROTATE_270) 139 if self._display[0] != 0: 140 img = img.resize((self._display[0], self._display[1])) 141 img = np.array(img) 142 height, width = img.shape[0], img.shape[1] 143 img[img < 20] = 0 144 img = img.reshape(-1) 145 screen_now_bytes = img.tobytes() 146 if screen_old_bytes is None: 147 screen_old_bytes = screen_now_bytes 148 start_pos = 0 149 end_pos = width * height * 4 150 else: 151 start_pos, end_pos = 3, 6 152 for i in range(width * height * 4): 153 if screen_now_bytes[i] != screen_old_bytes[i]: 154 start_pos = i 155 break 156 for i in range(width * height * 4 - 1, start_pos, -1): 157 if screen_now_bytes[i] != screen_old_bytes[i]: 158 end_pos = i + 1 159 break 160 screen_old_bytes = screen_now_bytes 161 print("\r|%s%s|" % ("=" * int(num / frame_count * 30), " " * (30 - int(num / frame_count * 30))), 162 "%.2f%%" % (num / frame_count * 100), end="", flush=True) 163 if start_pos == 3 or end_pos == 6: 164 output_bytes[offset:offset + 16] = struct.pack("IIII", 0, 0, 0, 0) 165 offset += 16 166 continue 167 compressed_bytes = zlib.compress(screen_old_bytes[start_pos:end_pos]) 168 raw_len = end_pos - start_pos 169 new_len = len(compressed_bytes) 170 output_bytes[offset:offset + 16] = struct.pack("IIII", 2, start_pos, raw_len, new_len) 171 offset += 16 172 output_bytes[offset:offset + new_len] = compressed_bytes 173 offset += new_len 174 while new_len % 4 != 0: 175 new_len += 1 176 output_bytes[offset:offset + 1] = b'\0' 177 offset += 1 178 179 if not os.path.exists(self._out): 180 os.makedirs(self._out) 181 with open(os.path.join(self._out, "bootanimation-%dx%d.raw" % (width, height)), "wb") as fp: 182 fp.write(output_bytes) 183 print("\nGenerate successfully!") 184 185 186def parse_option(): 187 parser = argparse.ArgumentParser(description="Make a boot video by a MP4 file or some .img files", 188 usage="python raw_maker.py (-m <*.mp4> | -i <directory>) [-o <directory>] " 189 "[-d <size>] [-r <angle>] [-f]\n" 190 " eg.: python raw_maker.py -i ./source/png -o ./out -d 640x480\n" 191 " python raw_maker.py -m ./animation.mp4 -o ./out -d 640x480") 192 exclusive_group = parser.add_mutually_exclusive_group(required=True) 193 exclusive_group.add_argument("-m", "--mp4", metavar="<*.mp4>", help="The input <*.mp4> file") 194 exclusive_group.add_argument("-i", "--image", metavar="<directory>", 195 help="The <directory> where image files are stored") 196 parser.add_argument("-o", "--out", metavar="<directory>", default=".", 197 help="Place generated .raw files into the <directory>") 198 parser.add_argument("-d", "--display", metavar="<size>", default="0x0", 199 help="Set the boot video display <size> and zoom the image, e.g.:640x480") 200 parser.add_argument("-r", "--rotate", metavar="<angle>", type=int, help="Rotate video <angle>, e.g.:90 180 270") 201 parser.add_argument("-f", "--flip", action="store_true", help="Flip the video", ) 202 return parser.parse_args() 203 204 205if __name__ == "__main__": 206 raw_maker = RawMaker(parse_option()) 207 raw_maker.make() 208