Rucha Katakwar | 222c531 | 2022-03-21 14:17:01 -0700 | [diff] [blame] | 1 | # Copyright 2022 The Android Open Source Project |
| 2 | # |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | # you may not use this file except in compliance with the License. |
| 5 | # You may obtain a copy of the License at |
| 6 | # |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | # |
| 9 | # Unless required by applicable law or agreed to in writing, software |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | # See the License for the specific language governing permissions and |
| 13 | # limitations under the License. |
| 14 | """Utility functions for processing video recordings. |
| 15 | """ |
| 16 | # Each item in this list corresponds to quality levels defined per |
| 17 | # CamcorderProfile. For Video ITS, we will currently test below qualities |
| 18 | # only if supported by the camera device. |
Rucha Katakwar | b6847db | 2022-03-24 16:49:13 -0700 | [diff] [blame] | 19 | import logging |
| 20 | import os.path |
| 21 | import subprocess |
Rucha Katakwar | b6847db | 2022-03-24 16:49:13 -0700 | [diff] [blame] | 22 | |
| 23 | |
Rucha Katakwar | 089e609 | 2022-03-30 11:19:26 -0700 | [diff] [blame] | 24 | ITS_SUPPORTED_QUALITIES = ( |
Rucha Katakwar | 41133f9 | 2022-03-22 13:44:09 -0700 | [diff] [blame] | 25 | 'HIGH', |
| 26 | '2160P', |
| 27 | '1080P', |
| 28 | '720P', |
| 29 | '480P', |
| 30 | 'CIF', |
| 31 | 'QCIF', |
| 32 | 'QVGA', |
| 33 | 'LOW', |
| 34 | 'VGA' |
Rucha Katakwar | 222c531 | 2022-03-21 14:17:01 -0700 | [diff] [blame] | 35 | ) |
Rucha Katakwar | b6847db | 2022-03-24 16:49:13 -0700 | [diff] [blame] | 36 | |
| 37 | |
Rucha Katakwar | 932bded | 2022-04-04 14:29:52 -0700 | [diff] [blame] | 38 | def extract_key_frames_from_video(log_path, video_file_name): |
Clemenz Portmann | 8d40e42 | 2022-04-28 10:29:01 -0700 | [diff] [blame] | 39 | """Returns a list of extracted key frames. |
Rucha Katakwar | b6847db | 2022-03-24 16:49:13 -0700 | [diff] [blame] | 40 | |
| 41 | Ffmpeg tool is used to extract key frames from the video at path |
Rucha Katakwar | 932bded | 2022-04-04 14:29:52 -0700 | [diff] [blame] | 42 | os.path.join(log_path, video_file_name). |
| 43 | The extracted key frames will have the name video_file_name with "_key_frame" |
Rucha Katakwar | b6847db | 2022-03-24 16:49:13 -0700 | [diff] [blame] | 44 | suffix to identify the frames for video of each quality.Since there can be |
| 45 | multiple key frames, each key frame image will be differentiated with it's |
| 46 | frame index.All the extracted key frames will be available in jpeg format |
| 47 | at the same path as the video file. |
| 48 | |
| 49 | Args: |
| 50 | log_path: path for video file directory |
Rucha Katakwar | 932bded | 2022-04-04 14:29:52 -0700 | [diff] [blame] | 51 | video_file_name: name of the video file. |
Rucha Katakwar | b6847db | 2022-03-24 16:49:13 -0700 | [diff] [blame] | 52 | Returns: |
| 53 | key_frame_files: A list of paths for each key frame extracted from the |
Clemenz Portmann | 8d40e42 | 2022-04-28 10:29:01 -0700 | [diff] [blame] | 54 | video. Ex: VID_20220325_050918_0_CIF_352x288.mp4 |
Rucha Katakwar | b6847db | 2022-03-24 16:49:13 -0700 | [diff] [blame] | 55 | """ |
Rucha Katakwar | 932bded | 2022-04-04 14:29:52 -0700 | [diff] [blame] | 56 | ffmpeg_image_name = f"{video_file_name.split('.')[0]}_key_frame" |
Clemenz Portmann | 8d40e42 | 2022-04-28 10:29:01 -0700 | [diff] [blame] | 57 | ffmpeg_image_file_path = os.path.join( |
| 58 | log_path, ffmpeg_image_name + '_%02d.png') |
Rucha Katakwar | b6847db | 2022-03-24 16:49:13 -0700 | [diff] [blame] | 59 | cmd = ['ffmpeg', |
Clemenz Portmann | 8d40e42 | 2022-04-28 10:29:01 -0700 | [diff] [blame] | 60 | '-skip_frame', |
| 61 | 'nokey', |
| 62 | '-i', |
| 63 | os.path.join(log_path, video_file_name), |
| 64 | '-vsync', |
| 65 | 'vfr', |
| 66 | '-frame_pts', |
| 67 | 'true', |
| 68 | ffmpeg_image_file_path, |
| 69 | ] |
| 70 | logging.debug('Extracting key frames from: %s', video_file_name) |
| 71 | _ = subprocess.call(cmd) |
Rucha Katakwar | b6847db | 2022-03-24 16:49:13 -0700 | [diff] [blame] | 72 | arr = os.listdir(os.path.join(log_path)) |
| 73 | key_frame_files = [] |
| 74 | for file in arr: |
| 75 | if '.png' in file and not os.path.isdir(file) and ffmpeg_image_name in file: |
| 76 | key_frame_files.append(file) |
Rucha Katakwar | 2c49c45 | 2022-05-19 14:29:03 -0700 | [diff] [blame^] | 77 | |
| 78 | if not len(key_frame_files): |
| 79 | raise AssertionError('No key frames extracted. Check source video.') |
| 80 | |
Rucha Katakwar | b6847db | 2022-03-24 16:49:13 -0700 | [diff] [blame] | 81 | return key_frame_files |
| 82 | |
| 83 | |
| 84 | def get_key_frame_to_process(key_frame_files): |
Clemenz Portmann | 8d40e42 | 2022-04-28 10:29:01 -0700 | [diff] [blame] | 85 | """Returns the key frame file from the list of key_frame_files. |
Rucha Katakwar | b6847db | 2022-03-24 16:49:13 -0700 | [diff] [blame] | 86 | |
| 87 | If the size of the list is 1 then the file in the list will be returned else |
| 88 | the file with highest frame_index will be returned for further processing. |
| 89 | |
| 90 | Args: |
| 91 | key_frame_files: A list of key frame files. |
| 92 | Returns: |
| 93 | key_frame_file to be used for further processing. |
| 94 | """ |
| 95 | key_frame_files.sort() |
| 96 | return key_frame_files[-1] |
Avichal Rakesh | f82d526 | 2022-04-22 16:24:18 -0700 | [diff] [blame] | 97 | |
| 98 | |
| 99 | def extract_all_frames_from_video(log_path, video_file_name, img_format): |
Clemenz Portmann | 8d40e42 | 2022-04-28 10:29:01 -0700 | [diff] [blame] | 100 | """Extracts and returns a list of all extracted frames. |
Avichal Rakesh | f82d526 | 2022-04-22 16:24:18 -0700 | [diff] [blame] | 101 | |
| 102 | Ffmpeg tool is used to extract all frames from the video at path |
| 103 | <log_path>/<video_file_name>. The extracted key frames will have the name |
| 104 | video_file_name with "_frame" suffix to identify the frames for video of each |
| 105 | size. Each frame image will be differentiated with its frame index. All |
| 106 | extracted key frames will be available in the provided img_format format at |
| 107 | the same path as the video file. |
| 108 | |
| 109 | Args: |
| 110 | log_path: str; path for video file directory |
| 111 | video_file_name: str; name of the video file. |
| 112 | img_format: str; type of image to export frames into. ex. 'png' |
| 113 | Returns: |
| 114 | key_frame_files: An ordered list of paths for each frame extracted from the |
| 115 | video |
| 116 | """ |
| 117 | logging.debug('Extracting all frames') |
| 118 | ffmpeg_image_name = f"{video_file_name.split('.')[0]}_frame" |
| 119 | logging.debug('ffmpeg_image_name: %s', ffmpeg_image_name) |
| 120 | ffmpeg_image_file_names = ( |
| 121 | f'{os.path.join(log_path, ffmpeg_image_name)}_%03d.{img_format}') |
| 122 | cmd = [ |
| 123 | 'ffmpeg', '-i', os.path.join(log_path, video_file_name), |
| 124 | ffmpeg_image_file_names |
| 125 | ] |
| 126 | _ = subprocess.call(cmd) |
| 127 | |
| 128 | file_list = sorted( |
| 129 | [_ for _ in os.listdir(log_path) if (_.endswith(img_format) |
| 130 | and ffmpeg_image_name in _)]) |
Rucha Katakwar | 2c49c45 | 2022-05-19 14:29:03 -0700 | [diff] [blame^] | 131 | if not len(file_list): |
| 132 | raise AssertionError('No frames extracted. Check source video.') |
| 133 | |
Avichal Rakesh | f82d526 | 2022-04-22 16:24:18 -0700 | [diff] [blame] | 134 | return file_list |