Douyin ad first frame: black at most 60%, check with stills

Douyin in-feed ads need a first frame that is at most 60% black. Pull stills from your render with Sume video inspect and measure the first frame before upload.

4 min readSume
All posts

Douyin in-feed ad video needs a first frame that is no more than 60% black, and you can check that before upload by pulling the first frame from your render with Sume video inspect and measuring it yourself. The figures below come from a secondary summary of the Oceanengine ad platform, so confirm them on the live page.

The requirements as summarized

Douyin in-feed video requirements (secondary summary, read 2026-10-03)
ItemRequirement
Size540x960
File sizeUp to 500 MB
Length5-60 s
AudioNeeds background music
First frameBlack at most 60%

Get the first frame as a still

Video inspect reads one clip already on media.sume.com and never re-encodes it. Pass frames with at set to [0] to get the still at time zero as a durable image artifact. The route defaults to sync mode and needs an Idempotency-Key. Import the clip first with the media import route; off-host URLs are rejected.

Set frames.max_edge to the source edge when you want an unscaled still; the default max edge is 768.

curl -X POST https://api.sume.com/v1/video-inspect \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: douyin-first-frame-001" \
  -d '{
    "video_url": "https://media.sume.com/artifacts/artf_demo/ad.mp4",
    "frames": { "at": [0], "format": "png", "max_edge": 960 }
  }'

Measure the black share

The platform's definition of black is not in the summary I read, so the threshold below is my own assumption: a pixel counts as black when its luma is under 16 of 255. It tells you whether you are near the limit; it is not the platform's verdict. The script needs Pillow.

from PIL import Image

def black_share(path: str, luma_max: int = 16) -> float:
    px = list(Image.open(path).convert('L').getdata())
    return sum(1 for v in px if v < luma_max) / len(px)

share = black_share('first_frame.png')
print(f'{share:.1%} black', 'OK' if share <= 0.60 else 'too dark')

Fix the size and the music in one render

Timeline 1.0 takes output width and height as even integers from 256 to 2160, so 540 by 960 is a valid output. A soundtrack field adds a music bed. The default output is 1080 by 1920, so set the output explicitly. Timeline costs $0.10 per output minute, and the docs say to confirm live in the catalog.

If the first frame is too dark, put a bright title card or product still as the first slot, since stills are static holds.

Sources

Related posts

More in Media tools

All Media tools posts

Written by Sume