Frame-Chaining AI Video: Building a 60-Second Film with GMI Cloud

I wanted to create a 60-second cinematic FPV travel film across the United States, moving from New York to San Francisco and Los Angeles. The goal wasn't simply to generate impressive AI footage. I wanted the entire film to feel like one continuous drone journey rather than a collection of unrelated clips stitched together in post-production. That introduced three practical challenges: Maintaining visual continuity across independently generated clips. Controlling camera movement, lighting, and transitions through prompts. Fixing individual shots without regenerating the entire video. I built the film using GMI Cloud's video generation models, frame chaining, and FFmpeg. The final output was exactly 60 seconds long, rendered at 1080p, with a targeted replacement for a shot that generated incorrectly. Here's how the pipeline worked, including the API issues, failed generations, and techniques that made the workflow more reliable. 1. Understanding the Duration Constrain

Frame-Chaining AI Video: Building a 60-Second Film with GMI Cloud

I wanted to create a 60-second cinematic FPV travel film across the United States, moving from New York to San Francisco and Los Angeles.

The goal wasn't simply to generate impressive AI footage. I wanted the entire film to feel like one continuous drone journey rather than a collection of unrelated clips stitched together in post-production.

That introduced three practical challenges:

  • Maintaining visual continuity across independently generated clips.
  • Controlling camera movement, lighting, and transitions through prompts.
  • Fixing individual shots without regenerating the entire video.

I built the film using GMI Cloud's video generation models, frame chaining, and FFmpeg.

The final output was exactly 60 seconds long, rendered at 1080p, with a targeted replacement for a shot that generated incorrectly.

Here's how the pipeline worked, including the API issues, failed generations, and techniques that made the workflow more reliable.

1. Understanding the Duration Constraint

The first step was checking the available models and their actual generation limits.

Rather than relying on model names or assumptions, I used GMI Cloud's get_model tool to inspect the supported parameters, duration limits, and pricing.

get_model({ model_id: "seedance-2-5-260628" })
get_model({ model_id: "wan3.0-video" })
get_model({ model_id: "veo-3.1-generate-001" })

The following figures reflect the catalog and pricing I observed during development in October 2026. Verify them against the current catalog before using them in production.

Model Maximum duration Observed 1080p price/sec Relevant capabilities
seedance-2-5-260628 30 seconds $0.569 First-frame and last-frame conditioning, reference images, audio generation
wan3.0-video 30 seconds $0.20 Frame conditioning, reference images, separate negative prompt
veo-3.1-generate-001 8 seconds $0.40 High photorealism, two-frame conditioning
kling-3.0-turbo-t2v 15 seconds $0.14 Text-to-video generation
seedance-2-0-260128-upscale 15 seconds $0.056 Budget-oriented option
veo-3.1-lite-generate-001 8 seconds $0.05 Lower-cost generation at supported resolutions

These capabilities and prices are model- and configuration-dependent. Check the current API schema before building around a particular parameter.

The key constraint was straightforward: no model in this selection could generate the entire 60-second film in one request.

I therefore split the project into two 30-second generations.

Fewer, longer clips reduce the number of boundaries that need to be managed. A six-clip pipeline introduces five joins, while a two-clip pipeline introduces just one.

However, clip length shouldn't be the only consideration. The structure of the film matters, too. If a story naturally contains three distinct locations or sequences, three shorter clips may be easier to control than two longer generations.

The objective is to minimize unnecessary joins without forcing unrelated scenes into one generation.

2. Prompt Engineering: Control the Camera Before the Scene

One of the biggest lessons from this project was that a video prompt needs to describe motion, not just visual content.

My initial prompts combined several events into short sequences:

  • Dive between skyscrapers.
  • Sweep toward the Hudson River.
  • Transition from sunrise to blue hour.
  • Race through city streets.
  • Introduce light trails.

The results were inconsistent. Architecture distorted, transitions became muddy, and the camera sometimes appeared to blend incompatible locations.

The problem was trying to control too many independent events in a single prompt.

My revised approach was to define a continuous camera movement with explicit scene timing.

The single-movement principle

Describe one continuous camera journey per generation, and use FFmpeg for editorial cuts.

This doesn't mean a longer generation must contain only one location. It means the transitions between locations should follow a physically understandable camera movement rather than asking the model to perform several unrelated edits.

For example, an aircraft wing can pass across the lens and temporarily obscure the entire frame. The next location can emerge as the wing clears.

That provides a visual transition the model can attempt to render as part of the camera movement.

Here's an excerpt from the prompt used for the film:

Ultra-realistic cinematic FPV drone travel film, 30 seconds, 16:9.

One continuous high-speed journey across the United States.
Maintain forward camera momentum throughout.

Photorealistic imagery, consistent teal-and-orange commercial
color grading, high dynamic range, natural film grain,
subtle horizontal anamorphic lens flare, and a wide 14mm lens.

Use smooth motion blur only during the fastest movements.

No text, captions, subtitles, logos, watermarks, or readable
signage.

SHOT 1 (0-7s):
Above New York City at sunrise. Low mist surrounds the
skyscrapers as the drone tips forward and dives between
glass towers. Golden light reflects across the facades.

SHOT 2 (7-15s):
The camera descends to street level and races down a wide
Manhattan avenue. Moving cars pass beneath the camera,
their headlights and taillights creating flowing light trails.

SHOT 3 (15-22s):
The drone rises over New York Harbor. A ferry crosses the
water below, leaving a white wake. The camera sweeps around
to reveal the Statue of Liberty in the morning haze.

SHOT 4 (22-30s):
An aircraft wing passes directly across the lens, filling
the frame. As it clears, the camera emerges above turquoise
Pacific water near San Francisco, moving toward the Golden
Gate Bridge as its red towers appear through coastal fog.

The timing provides structure, but it doesn't guarantee that the model will follow every boundary precisely. Treat shot durations as guidance and inspect the generated footage before editing.

Keep the visual identity consistent

Consistency doesn't require identical lighting throughout the film.

The sequence moves between sunrise, daylight, golden hour, and night. Those changes are part of the story.

What should remain relatively stable is the visual treatment:

  • Color grading.
  • Lens characteristics.
  • Motion style.
  • Contrast and highlight handling.
  • Overall level of realism.

I repeated the same visual descriptors across prompts while allowing the lighting and environment to change.

This gave the film a more consistent look without making every location feel artificially identical.

Negative prompts are model-specific

Different models expose different controls.

For example, wan3.0-video supports a dedicated negative_prompt parameter, while the Seedance 2.5 configuration I used did not expose that parameter.

For Seedance, I placed unwanted elements directly in the prompt:

No text, captions, subtitles, logos, watermarks, or readable
signage anywhere in the frame.

The general lesson is to inspect the actual model schema rather than assume that every video model supports the same parameters.

3. Frame Chaining: Maintaining Continuity Between Clips

Frame chaining was the central technique in this project.

Instead of generating two unrelated clips and trying to hide their differences afterward, I used the final frame of the first clip to condition the beginning of the second.

The pipeline is simple:

  1. Generate clip A.
  2. Extract a frame near the end of clip A.
  3. Upload the frame to GMI Cloud.
  4. Pass its URL as the first_frame parameter when generating clip B.
  5. Inspect the boundary and adjust the edit if necessary.

The second generation now has a visual reference for where the first generation ended.

Extracting the frame with FFmpeg

I extracted a frame near the end of the first clip:

ffmpeg -y -ss 29.9 -i clipA.mp4 \
  -frames:v 1 chain_last.png

I then normalized the image to the intended output dimensions and converted it to JPEG:

ffmpeg -y -i chain_last.png \
  -vf "scale=1920:1080:force_original_aspect_ratio=increase,crop=1920:1080" \
  -q:v 3 chain_first.jpg

The output dimensions should match the intended video aspect ratio. If the source frame already has the correct dimensions, unnecessary scaling and cropping can be avoided.

The extracted image becomes the conditioning frame for the next generation.

Uploading the frame to GMI Cloud

The upload_media operation returns a signed upload_url and a public_url.

These serve different purposes:

  • upload_url is the destination for uploading the file.
  • public_url is the image URL supplied to the video generation request.

The signed URL doesn't upload the file automatically. You must send the image bytes to it.

Here's a minimal Python example:

import urllib.request

upload_url = ""

with open("chain_first.jpg", "rb") as image_file:
    image_data = image_file.read()

request = urllib.request.Request(
    upload_url,
    data=image_data,
    method="PUT",
    headers={"Content-Type": "image/jpeg"},
)

with urllib.request.urlopen(request) as response:
    print(response.status)

Use the URL and HTTP method specified by the upload operation. If the signed URL expires or the upload returns a signature error, request a fresh URL and retry.

I encountered a 403 SignatureDoesNotMatch error with an earlier upload URL. Requesting a new URL resolved the problem in my case.

Generating the next clip

With the frame uploaded, I passed its public URL to Seedance 2.5.

The following is a representative request structure; use the exact field names and request envelope documented by the current API.

{
  "model_id": "seedance-2-5-260628",
  "prompt": "",
  "parameters": {
    "duration": 30,
    "resolution": "1080p",
    "ratio": "adaptive",
    "first_frame": "",
    "generate_audio": true,
    "watermark": false
  }
}

The critical setting was ratio.

The ratio configuration issue

I encountered failed generations when using an explicit ratio: "16:9" and when omitting the ratio entirely.

The API returned an error explaining that, when first-frame or first-and-last-frame conditioning is used, the output aspect ratio follows the input frame.

The configuration that worked was:

"ratio": "adaptive"

For this particular model configuration, using ratio: "adaptive" resolved the issue.

Don't assume that a parameter's default value is valid for every generation mode. Frame-conditioned generation can have different validation rules from text-to-video generation.

Verify this behavior against the current API documentation before relying on it in another model version.

What frame chaining actually guarantees

Frame chaining is useful, but it is important to distinguish three different forms of continuity.

Frame continuity: The next generation receives a reference image matching the preceding clip's endpoint.

Temporal continuity: Camera motion, object movement, lighting, and perspective evolve naturally across the boundary.

Subject continuity: People, vehicles, and other objects maintain their identity and appearance across shots.

Frame chaining primarily addresses the first problem. It can help with the second, but it doesn't guarantee a perfect motion transition. It also doesn't guarantee subject consistency across independently generated scenes.

A matching boundary frame is not proof that the surrounding motion will be seamless.

For recurring subjects, combine frame conditioning with reference images where the model supports them.

Model Reference parameter Observed catalog limit
wan3.0-video reference_image, reference_video Up to 10 images and 5 videos
seedance-2-5-260628 reference_images, reference_videos Up to 9 images and 3 videos
happyhorse-1.1-r2v reference_image Up to 5 images

These limits are model-specific and should be verified before use.

I also found that frame chaining works best when the next clip continues a compatible scene.

A coastline transitioning into a bridge approach provides a natural continuation. A sudden jump from San Francisco to a Los Angeles street is a different editorial event and is often better handled as a deliberate cut.

Chain compatible camera movements. Use editing to handle genuine changes in scene or location.

4. Stitching the Film with FFmpeg

Once the clips were generated, I assembled them into the final video.

For a simple join, FFmpeg's concat demuxer is convenient when the input files have compatible stream properties.

Create a file named list.txt:

file 'clipA.mp4'
file 'clipB.mp4'

Then concatenate the clips:

ffmpeg -y -f concat -safe 0 -i list.txt \
  -t 60.0 \
  -vf "fps=24,scale=1920:1080:flags=lanczos,format=yuv420p" \
  -c:v libx264 -preset slow -crf 17 \
  -c:a aac -movflags +faststart \
  out/master.mp4

This example assumes that the input streams are compatible with the chosen filter and encoding pipeline. Incompatible codecs, time bases, resolutions, or audio streams may require normalization before concatenation.

The command also re-encodes the video, so it is not a lossless join.

When the clips need different processing

For the final replacement, I needed to combine a 50-second section of the existing master with a newly generated 10-second clip.

I used a filter graph to concatenate the video and audio streams while normalizing the video output:

ffmpeg -y -i head.mp4 -i clipFix.mp4 \
  -filter_complex \
  "[0:v][1:v]concat=n=2:v=1:a=0,fps=24,scale=1920:1080:flags=lanczos,format=yuv420p[v]; \
   [0:a][1:a]concat=n=2:v=0:a=1[a]" \
  -map "[v]" -map "[a]" \
  -t 60.0 \
  -c:v libx264 -preset slow -crf 17 \
  -profile:v high -level 4.2 \
  -c:a aac -b:a 192k \
  -movflags +faststart \
  out/america_60s_16x9_v2.mp4

This version assumes both inputs contain compatible audio streams. If one clip lacks audio, the filter graph needs to be adjusted to create or omit the missing stream.

Also, -t 60.0 limits the output duration; it doesn't independently guarantee that the final file contains exactly 60 seconds of usable footage.

To verify the result, inspect the actual duration and frame count.

ffprobe -v error \
  -show_entries format=duration \
  -of default=noprint_wrappers=1:nokey=1 \
  out/america_60s_16x9_v2.mp4

For a 24 fps output, 60 seconds corresponds to 1,440 frames. Checking both duration and frame count is useful when exact delivery specifications matter.

Creating a cinematic 2.39:1 presentation

The source video was generated at 16:9, but I also wanted to test a wider cinematic presentation.

One option is to scale the image to 1920 × 804 and add black bars to restore a 1920 × 1080 canvas:

ffmpeg -y -i master.mp4 \
  -vf "scale=1920:804:flags=lanczos,pad=1920:1080:0:138:black" \
  -c:v libx264 -preset slow -crf 17 \
  -profile:v high \
  -c:a copy \
  -movflags +faststart \
  out/master_239.mp4

This produces an approximately 2.39:1 image area within a 16:9 video frame.

Letterboxing preserves the entire scaled image, unlike a direct vertical crop that discards parts of the original frame.

However, scaling the full 16:9 image to 1920 × 804 changes its proportions unless the image is cropped first. For a geometrically correct widescreen presentation, use a crop-and-scale pipeline when necessary.

5. Re-Rendering One Bad Shot Instead of the Entire Film

One of the most useful aspects of this workflow was the ability to replace a specific segment without rebuilding the whole video.

During generation, the Hollywood sign appeared as "HOLFWOOD" instead of "HOLLYWOOD".

This is a familiar limitation of generative video models: text can look convincing at a glance while individual characters remain incorrect or unstable.

Regenerating the entire 60-second film would have wasted both time and credits.

Instead, I extracted the frame at the replacement boundary, used it as the new clip's first_frame, and generated a replacement segment.

ffmpeg -y -ss 50.0 -i out/master.mp4 \
  -frames:v 1 splice_first.jpg

After uploading the frame and generating the replacement clip, I rebuilt the master from the unchanged opening section and the new footage.

The replacement was approximately 10 seconds long.

Prompting for accurate text

For the replacement, I made the lettering requirement explicit:

CRITICAL: The hillside sign must read exactly HOLLYWOOD.

The spelling is H-O-L-L-Y-W-O-O-D: nine letters.

The fourth letter must be L, not F.

The letters are large, white, individually formed capital
letters arranged in a straight horizontal line on a dry,
brown hillside.

Keep the lettering stable, correctly formed, and clearly
visible from the front. Do not distort, rearrange, or replace
any characters.

The revised generation produced the intended spelling.

This doesn't mean literal prompts can reliably solve text rendering in every model. It means a specific description of the expected output and known failure can improve the next attempt.

For shots containing signs, product labels, or other critical text, I recommend treating one or two targeted re-renders as part of the production budget.

When exact typography is essential, a more reliable approach is to add the text during post-production rather than depend on the video model to render it correctly.

6. Practical Issues I Encountered

The creative workflow was only part of the implementation. Several API and environment issues also affected the process.

Keep generation parameters in the expected structure

My generation-estimation requests required model-specific parameters to be nested under parameters.

For example:

{
  "model_id": "seedance-2-5-260628",
  "prompt": "",
  "parameters": {
    "duration": 30,
    "resolution": "1080p",
    "ratio": "adaptive"
  }
}

Use the structure expected by the relevant operation. An estimation endpoint and a generation endpoint may not accept identical request bodies.

An MCP timeout doesn't necessarily mean generation failed

One submit_generation request timed out on the client side while the server-side generation continued running.

Submitting the same request again could have created unnecessary work or additional charges.

Instead, I used list_generations to inspect the existing jobs and recover their status.

For production systems, keep track of generation IDs, request parameters, and job status. When supported, use idempotency controls to distinguish intentional re-renders from accidental duplicate submissions.

Check which credits apply to each model

My account displayed a zero-dollar balance alongside promotional credits. One model returned an insufficient_balance error, while another accepted the request.

This was an account-specific result, not proof that all promotional credits are restricted in the same way.

Before running a large batch, verify that the available credits apply to the selected model and generation configuration.

Verify your FFmpeg version

My system FFmpeg installation was too old to handle the HEVC Main10 output produced by the video model.

The resulting codec errors initially looked like a problem with the downloaded file.

I installed a newer FFmpeg binary through imageio-ffmpeg:

pip install imageio-ffmpeg

Then I checked the binary path:

python -c "import imageio_ffmpeg; print(imageio_ffmpeg.get_ffmpeg_exe())"

Use a sufficiently recent FFmpeg build with the codecs required by your source footage.

For reproducible workflows, check the installed version and codec support before beginning a batch of generations.

Verify audio instead of trusting the response

Although I requested audio generation, both clips returned valid AAC audio streams that were effectively silent.

I checked the audio levels with:

ffmpeg -i clip.mp4 \
  -af volumedetect \
  -f null - 2>&1 | grep -E "mean_volume|max_volume"

A valid audio stream doesn't necessarily contain audible sound. Inspect the output and listen to the actual result before delivery.

Separate language-model usage from media-generation costs

The account-summary information I inspected reported language-model usage separately from image and video generation.

Don't treat an LLM token-cost report as a complete accounting of a media-generation project.

Track the actual generation requests and their associated charges, and reconcile them against the relevant billing information.

7. Cost Breakdown

The final film used two 30-second clips and one 10-second replacement.

Generation Duration Observed cost
Clip A — Seedance 2.5, 1080p 30 seconds $17.07
Clip B — Seedance 2.5, 1080p, frame-conditioned 30 seconds $17.07
Replacement clip — Seedance 2.5, 1080p 10 seconds $5.69
Total 70 generated seconds $39.83

The total cost was $39.83 for a final 60-second film.

Notice that the generation duration adds up to 70 seconds, not 60. The extra 10 seconds came from replacing the incorrect segment.

The effective cost was approximately $0.66 per second of finished video.

These figures reflect the pricing and usage observed during this project. They exclude any additional costs not reflected in the recorded generation charges.

A lower-cost model may make experimentation more affordable, but cheaper generation can also require additional attempts to reach the desired visual quality.

The right choice depends on the importance of photorealism, continuity, generation speed, and budget.

Budget for re-renders from the beginning. A pipeline that assumes every generated shot will be usable is likely to underestimate its actual cost.

8. Scaling the Pipeline Beyond 60 Seconds

The same approach can be extended to longer films.

At a maximum of 30 seconds per clip:

Final duration Number of clips Number of joins
60 seconds 2 1
3 minutes 6 5
5 minutes 10 9
10 minutes 20 19

These counts assume full-length 30-second clips. Shorter clips may be more appropriate when the story contains distinct scenes or requires tighter control.

Each additional join creates another opportunity for mismatched motion, lighting, composition, or subject appearance.

Several practices make a longer pipeline easier to manage.

Choose boundary frames deliberately

Don't extract the final frame automatically without checking it.

Look for a frame where the camera is passing behind an object, approaching a new landmark, or entering a natural visual transition.

These moments can make an editorial boundary less noticeable.

However, the extracted frame should also provide a useful starting composition for the next generation. A visually interesting boundary is not necessarily a good conditioning frame if the next scene cannot continue naturally from it.

Preserve intermediate frames and generation metadata

Archive the conditioning frame used for every clip, along with:

  • The original prompt.
  • Model ID and generation parameters.
  • Generation job ID.
  • Source and output file paths.
  • Cost and duration.
  • Notes about failed attempts.

If clip 4 needs to be regenerated, the saved endpoint from clip 3 allows you to reproduce its starting condition without rebuilding the earlier footage.

This makes the workflow easier to debug and maintain.

Parallelize independent generations

Once the necessary reference frames are available, independent segments can be generated concurrently.

A fully sequential chain cannot be generated entirely in parallel because each clip may depend on the preceding clip's output.

A practical compromise is to divide the film into sequences that can be generated independently, then chain clips within each sequence.

Use idempotency controls where supported, and check the job list before retrying a timed-out request.

Make every segment independently replaceable

Treat each generated clip as a separate production asset.

The master video should be a reproducible output of the individual segments and the editing pipeline, not the only copy of the project.

That allows a single failed shot to be replaced without rebuilding the entire film.

9. Visual Verification Before Delivery

A successful API response doesn't mean the video is ready to publish.

I used FFmpeg to extract representative frames from across the timeline and assemble them into a contact sheet.

This made it easier to identify changes in visual style, unexpected objects, and problems with transitions.

For example, to inspect eight points in the film:

mkdir -p keys

for t in 1 9 17 25 33 41 49 58; do
  ffmpeg -y -ss "$t" -i out/master.mp4 \
    -frames:v 1 \
    -vf "scale=470:-1" \
    "keys/m_${t}.jpg"
done

The individual frames can then be assembled into a contact sheet.

A contact sheet is useful for reviewing the overall visual progression, but it won't reveal every temporal artifact.

For that, inspect the actual footage around each join, preferably frame by frame or at reduced playback speed.

For the Hollywood sign, I also extracted frames at half-second intervals to check whether the lettering remained stable throughout the replacement segment.

This helped distinguish incorrect lettering from frames where the sign was partially visible or viewed at an angle.

My final verification checklist included:

  • [ ] Confirm the final duration and frame rate.
  • [ ] Inspect every join for jumps in camera position or motion.
  • [ ] Check lighting, color grading, and scene continuity.
  • [ ] Review text, faces, architecture, and other high-risk details.
  • [ ] Verify audio levels and playback.
  • [ ] Confirm the output resolution and encoding.
  • [ ] Play the final exported file from beginning to end.

The model generates the footage. The editing and verification pipeline determines whether it is ready to ship.

Conclusion

Building a continuous AI-generated film is not just a prompting problem. It is a pipeline problem involving generation limits, frame conditioning, asset management, editing, and quality control.

For this project, frame chaining provided a practical way to connect separately generated clips while keeping each segment independently replaceable.

The most important lessons were:

  1. Check model capabilities before designing the pipeline. Generation limits and parameter support determine how the film should be structured.
  2. Describe camera movement explicitly. A coherent camera path is easier to manage than a prompt containing unrelated transitions.
  3. Use frame chaining for continuity. The previous clip's endpoint provides a visual starting condition for the next generation.
  4. Keep the pipeline modular. Replace failed segments rather than regenerating the entire film.
  5. Verify the actual output. Inspect the joins, text, audio, duration, and final encoding.
  6. Budget for failed generations. Re-renders are part of the workflow, not an unexpected exception.

The final film cost $39.83 in recorded generation charges and required 70 seconds of generated footage to produce 60 seconds of finished video.

The broader takeaway is that AI video generation becomes more manageable when treated as a reproducible media pipeline rather than a sequence of isolated prompts.

Frame chaining won't eliminate every continuity problem, but it gives you a useful degree of control over how separately generated footage fits together.

Model capabilities, parameter support, and pricing are subject to change. The figures in this article reflect observations from October 2026. Check the current GMI Cloud model catalog and API documentation before implementing the workflow.

Share