• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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