Guide

Google ended AutoFlip support in 2023, and pip ships none of it. Rebuild its shot-by-shot 9:16 crop in Python with MediaPipe and PySceneDetect, or let clipsdk follow the speaker for $0.03 per source minute.

AutoFlip alternative: rebuild the 9:16 crop in Python

AutoFlip still needs a Bazel build and OpenCV 3, and the mediapipe package on pip ships none of it, so this guide rebuilds its core idea with the MediaPipe Tasks face detector.

GuidesUpdated

Or one API call

$0.03per source minute

  • Every clip included, up to 1080p60 and 20 Mb/s
  • $1 free to start, 33 minutes of video
  • Prepaid credit, no subscription
See pricing

AutoFlip, the rebuild and clipsdk side by side

AutoFlip, the rebuild and clipsdk side by side
OptionStatusShot cutsFacesCameraCost
AutoFlip (MediaPipe legacy)Support ended March 1, 2023Its own shot boundary calculatorFaces plus other salient objectsSteady or following, decided per shotFree, you build it with Bazel
The rebuild in this guideRuns on current PythonPySceneDetectBlazeFace full-range in MediaPipe TasksOne steady crop per shotFree, runs on your CPU
clipsdkLive APIffmpeg scdet at threshold 8, plus fadesYuNet 5 times a second, speaker picked by mouth motionCuts on speaker switches, eases in between$0.03 per source minute

Rebuild AutoFlip in Python

We ran every file below against NASA's Rocket Ranch podcast, one of our public domain demo videos. The output each file printed sits under its code.

  1. Install the pieces and cut a test moment

    AutoFlip is not in the pip package: not one file that mediapipe installs has autoflip in its name. Install the MediaPipe Tasks API and PySceneDetect instead, fetch both BlazeFace models and cut the 59-second moment clipsdk ranked as its second clip.

    setup.sh
    python3 -m venv venv && . venv/bin/activate
    pip install -q mediapipe==1.2.2 scenedetect==0.7.2
    curl -sO https://api.clipsdk.com/f/lib/demo/rocket-ranch-podcast.mp4
    for model in full_range short_range; do
      curl -sO https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_$model/float16/1/blaze_face_$model.tflite
    done
    ffmpeg -v error -ss 186.31 -i rocket-ranch-podcast.mp4 -t 59.13 -c:v libx264 -crf 18 -c:a aac moment.mp4
    
    
    # $ pip show -f mediapipe | grep -i -c autoflip
    # 0
    
    
    # $ python3 --version && ffmpeg -version | head -1
    # Python 3.13.14
    # ffmpeg version 7.1.5-0+deb13u1 Copyright (c) 2000-2026 the FFmpeg developers
    
  2. Split the shots, then pick one crop per shot

    AutoFlip decides per shot whether the camera holds still or follows the action. People at a podcast table barely move, so a steady crop per shot does the job. PySceneDetect finds the cuts, BlazeFace finds the biggest face 5 times in each shot, the median face centre sets a 608x1080 crop, and one ffmpeg pass switches crops at every cut.

    reframe.py
    import subprocess
    import cv2
    import mediapipe as mp
    from mediapipe.tasks.python import BaseOptions, vision
    from scenedetect import ContentDetector, detect
    
    SRC, OUT = "moment.mp4", "vertical.mp4"
    
    # AutoFlip step 1: split the video into shots at every camera cut
    shots = detect(SRC, ContentDetector())
    
    video = cv2.VideoCapture(SRC)
    W = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
    H = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
    crop_w = round(H * 9 / 16 / 2) * 2
    faces = vision.FaceDetector.create_from_options(vision.FaceDetectorOptions(
        base_options=BaseOptions(model_asset_path="blaze_face_full_range.tflite")))
    
    def biggest_face_x(t):
        video.set(cv2.CAP_PROP_POS_MSEC, t * 1000)
        ok, frame = video.read()
        image = mp.Image(image_format=mp.ImageFormat.SRGB, data=cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
        found = faces.detect(image).detections
        if not found:
            return None
        box = max(found, key=lambda d: d.bounding_box.width).bounding_box
        return box.origin_x + box.width / 2
    
    # AutoFlip step 2: one steady crop per shot, its "stable camera" choice
    plan = []
    for start, end in shots:
        a, b = start.seconds, end.seconds
        xs = [x for i in range(5) if (x := biggest_face_x(a + (b - a) * (i + 0.5) / 5)) is not None]
        centre = sorted(xs)[len(xs) // 2] if xs else W / 2
        left = int(min(max(centre - crop_w / 2, 0), W - crop_w))
        plan.append((b, left))
        print(f"shot {a:5.2f}-{b:5.2f}s  face in {len(xs)}/5 samples  crop x={left}")
    
    # AutoFlip step 3: render, switching the crop at each cut
    x_expr = str(plan[-1][1])
    for end, left in reversed(plan[:-1]):
        x_expr = f"if(lt(t,{end:.3f}),{left},{x_expr})"
    subprocess.run(["ffmpeg", "-v", "error", "-y", "-i", SRC,
                    "-vf", f"crop={crop_w}:{H}:'{x_expr}':0,scale=1080:1920",
                    "-c:a", "copy", OUT], check=True)
    print(f"{len(plan)} shots, {crop_w}x{H} crop scaled to 1080x1920: {OUT}")
    
    # $ time python3 reframe.py 2>/dev/null
    # shot  0.00- 5.47s  face in 5/5 samples  crop x=1256
    # shot  5.47-17.73s  face in 5/5 samples  crop x=476
    # shot 17.73-23.13s  face in 5/5 samples  crop x=818
    # shot 23.13-30.73s  face in 5/5 samples  crop x=41
    # shot 30.73-35.73s  face in 5/5 samples  crop x=474
    # shot 35.73-39.23s  face in 5/5 samples  crop x=1257
    # shot 39.23-50.73s  face in 5/5 samples  crop x=426
    # shot 50.73-59.10s  face in 4/5 samples  crop x=38
    # 8 shots, 608x1080 crop scaled to 1080x1920: vertical.mp4
    #
    # real	2m32.576s
    # user	3m57.115s
    # sys	0m12.353s
    
  3. Use the full-range face model

    Google tunes the short-range model for selfie distance and the full-range model for back camera shots. On this wide two-person studio shot that matters: short-range missed the face in 4 of every 10 sampled frames, while full-range found it in all of them.

    compare_detectors.py
    import cv2
    import mediapipe as mp
    from mediapipe.tasks.python import BaseOptions, vision
    
    video = cv2.VideoCapture("moment.mp4")
    detectors = {
        name: vision.FaceDetector.create_from_options(vision.FaceDetectorOptions(
            base_options=BaseOptions(model_asset_path=f"blaze_face_{name}.tflite")))
        for name in ("short_range", "full_range")
    }
    
    hits = dict.fromkeys(detectors, 0)
    for second in range(59):
        video.set(cv2.CAP_PROP_POS_MSEC, second * 1000 + 500)
        ok, frame = video.read()
        image = mp.Image(image_format=mp.ImageFormat.SRGB, data=cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
        for name, detector in detectors.items():
            hits[name] += len(detector.detect(image).detections) > 0
    
    print({name: f"face found in {count}/59 frames" for name, count in hits.items()})
    
    # $ python3 compare_detectors.py 2>/dev/null
    # {'short_range': 'face found in 35/59 frames', 'full_range': 'face found in 59/59 frames'}
    

Or send one request

The same podcast through clipsdk with reframe: "auto" came back as 3 ranked 9:16 clips in under 2 minutes. Clip 2 is the moment from the steps, and it scored 85.

clipsdk finds faces with YuNet 5 times a second and picks the speaker by mouth motion, so a two-person shot crops to whoever is talking. A shot with no face keeps the whole frame over a blurred copy instead of a blind centre crop.

POST/v1/clipsTry it
curl https://api.clipsdk.com/v1/clips \
  -H "Authorization: Bearer $CLIPSDK_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "video_url": "https://api.clipsdk.com/f/lib/demo/rocket-ranch-podcast.mp4",
    "clips": 3,
    "aspect": "9:16",
    "reframe": "auto",
    "captions": "bold"
  }'
Clip 2, 59 seconds, score 85: reframed around whoever is talking, with bold captions.clip-2.mp4clip-2.jpg

What AutoFlip did, and what is left of it

AutoFlip was a MediaPipe graph that turned landscape video into any aspect ratio. It found shot boundaries, detected faces and other salient objects, and decided per shot between a camera that holds still and one that follows the action.

Google's AutoFlip page now opens with one line: "We have ended support for this MediaPipe Legacy Solution as of March 1, 2023." The C++ graph still builds with bazel build, and by the same page it only works with OpenCV 3. The current mediapipe package on pip has the Tasks API and no AutoFlip.

Where the simple rebuild falls short

A steady crop per shot can't follow someone who walks across the frame, and when two people share a shot the biggest face wins, not the one talking. With no face found the script centres the crop, where AutoFlip would pad the frame to keep its salient content.

Each fix is more code: track faces between frames, score mouth movement to find the speaker, ease the crop between targets, and catch fades as well as hard cuts. That is the part clipsdk runs for you, as a reframe API or inside every clip job.

What it costs

The rebuild is free software, so the cost is your CPU: reframe.py took about two and a half minutes for the 59-second moment.

clipsdk bills per source minute, so the episode, just under 10 minutes, cost about $0.29 at $0.03 for the 3 clips above, with no subscription. See pricing for the clip minute floor and volume rate.

Questions, answered

Is MediaPipe AutoFlip still supported?

No. Google's AutoFlip page says support for this MediaPipe Legacy Solution ended on March 1, 2023, and points to MediaPipe Solutions instead. The code is still in the repository, but it gets no fixes.

Is AutoFlip free?

Yes. The source is free in Google's MediaPipe repository, but you compile it yourself with Bazel and it needs OpenCV 3. The Python rebuild on this page is free too and installs with pip. clipsdk costs $0.03 per source minute, and your first $1 is free.

Why is AutoFlip not working?

The usual blockers are the build and the package. AutoFlip is a C++ graph you compile with Bazel, Google's page says it only works with OpenCV 3, and the mediapipe package on pip doesn't include it, so pip install mediapipe gives you nothing named AutoFlip to run.

Which MediaPipe face model works for wide shots?

Full-range. On a two-person studio shot it found a face in every frame we sampled, while short-range, which Google tunes for selfie distance, missed 4 in 10.

Start free

Your back catalog is full of shorts

Your first $1 is free: 33 minutes of video, every clip included up to 1080p60 and 20 Mb/s.

Two studio microphones on a desk in a dark room lit by a red glow