Use the original and target frame rates and a pull-down technique to create a mapping between the original fields and frames or a video to a new frame rate. This might repeat or skip fields and frames of the original video, depending on whether the frame rate is increasing or decreasing.
| 113 | // This might repeat or skip fields and frames of the original video, depending on |
| 114 | // whether the frame rate is increasing or decreasing. |
| 115 | void FrameMapper::Init() |
| 116 | { |
| 117 | ZmqLogger::Instance()->AppendDebugMethod("FrameMapper::Init (Calculate frame mappings)"); |
| 118 | |
| 119 | // Do not initialize anything if just a picture with no audio |
| 120 | if (info.has_video and !info.has_audio and info.has_single_image) |
| 121 | // Skip initialization |
| 122 | return; |
| 123 | |
| 124 | // Prevent async calls to the following code |
| 125 | const std::lock_guard<std::recursive_mutex> lock(getFrameMutex); |
| 126 | |
| 127 | // Clear the fields & frames lists |
| 128 | Clear(); |
| 129 | |
| 130 | // Find parent position (if any) |
| 131 | Clip *parent = static_cast<Clip *>(ParentClip()); |
| 132 | if (parent) { |
| 133 | parent_position = parent->Position(); |
| 134 | parent_start = parent->Start(); |
| 135 | } else { |
| 136 | parent_position = 0.0; |
| 137 | parent_start = 0.0; |
| 138 | } |
| 139 | |
| 140 | // Mark as not dirty |
| 141 | is_dirty = false; |
| 142 | |
| 143 | // Clear cache |
| 144 | final_cache.Clear(); |
| 145 | |
| 146 | // Some framerates are handled special, and some use a generic Keyframe curve to |
| 147 | // map the framerates. These are the special framerates: |
| 148 | if ((fabs(original.ToFloat() - 24.0) < 1e-7 || fabs(original.ToFloat() - 25.0) < 1e-7 || fabs(original.ToFloat() - 30.0) < 1e-7) && |
| 149 | (fabs(target.ToFloat() - 24.0) < 1e-7 || fabs(target.ToFloat() - 25.0) < 1e-7 || fabs(target.ToFloat() - 30.0) < 1e-7)) { |
| 150 | |
| 151 | // Get the difference (in frames) between the original and target frame rates |
| 152 | float difference = target.ToInt() - original.ToInt(); |
| 153 | |
| 154 | // Find the number (i.e. interval) of fields that need to be skipped or repeated |
| 155 | int field_interval = 0; |
| 156 | int frame_interval = 0; |
| 157 | |
| 158 | if (difference != 0) |
| 159 | { |
| 160 | field_interval = round(fabs(original.ToInt() / difference)); |
| 161 | |
| 162 | // Get frame interval (2 fields per frame) |
| 163 | frame_interval = field_interval * 2.0f; |
| 164 | } |
| 165 | |
| 166 | |
| 167 | // Calculate # of fields to map |
| 168 | int64_t frame = 1; |
| 169 | int64_t number_of_fields = reader->info.video_length * 2; |
| 170 | |
| 171 | // Loop through all fields in the original video file |
| 172 | for (int64_t field = 1; field <= number_of_fields; field++) |