PAG官网 | PAG动效

PAG官网 | PAG动效

  • 首页
  • 产品
  • 功能
  • 文档
  • 案例
  • CN
  • GitHub
  • 论坛交流
  • 免费下载
  • Languages iconEN
    • CN

›TAVMedia

了解 PAG

  • Introduction
  • FAQs

快速开始

  • Install PAGViewer
  • Install PAGExporter
  • Export PAG Files
  • SDK Integration

导出插件

  • Use Configuration Panel
  • Use Exporting Panel
  • Export BMP Compositions
  • Config Fill Modes
  • Config Time Stretch Modes
  • Exporting Shortcut Keys
  • Error Code
  • Auto Detection Rules
  • Text Editing Rules
  • Add Text Background
  • Export Audio
  • Manually Install PAGExporter

预览工具

  • Preview Replacements
  • View File Structure
  • Preview Shortcut Keys
  • Export Image Sequence
  • File Encryption
  • Add Watermark
  • Upgrade to Beta Version

性能优化

  • Use Performance Panel
  • PAG File Optimization

移动端进阶

  • Common API Overview
  • Use PAGImageView
  • Video Replacement
  • Play Audio
  • Text Layer Description
  • Use Encripted File
  • Export To Video
  • SDK Authentication

Web 进阶

  • SDK Installation
  • Load PAG File
  • Play PAG File
  • Platform Capabilities
  • Use WebWorker

API 参考

  • API Document

视频教程

  • PAG Workflow
  • File Optimization Best Practices
  • Use PAGExporter Panel
  • PAG Online Q&A

资源下载

  • PAGViewer Installer
  • PAG Test Files
  • PAG Demo Projects
  • China LiveVideoStackCon2022
  • PAG Conversion Tool
  • PAG File Format Spec

TAVMedia

  • Introduction to TAVMedia
  • TAVMedia Quick access
  • Common API Overview

其他

  • From Lottie To PAG
  • PAG Dictionary

Common API Overview

TAVMedia Authentication

Authentication is required before using the TAVMedia SDK. Authentication only needs to be performed once during the app startup process. It is recommended to perform it in the callback of the app startup. For specific authentication operations, please refer to the License Guide. If it has not been authenticated or the authentication failed, a constantly moving label will be displayed on the screen.

Basic function

The main functions of TAVMedia SDK consist of TAVClip, TAVSurface, TAVVideoReader and TAVAudioReader.

TAVClip

TAVClip is the structure of playback data, used to describe the playback content, occurrence time, and location. The types of Clips mainly include the following two:

  • -Media Type: Usually can be played directly, including Audio, Movie, Composition, and PAG Sticker.

  • Effect type: used for special effects processing on Clips, including audio fade-in and fade-out, Clip position transformation, PAG effects, LUT, saturation and brightness adjustment, etc.

TAVSurface

TAVSurface is a rendering canvas used for final rendering of the image. It can be created through CAEAGLLayer and CVPixelBuffer in iOS.

TAVVideoReader

TAVVideoReader is a rendering controller used to control the drawing progress and, together with TAVSurface, controls the playback of TAVClip images.

Use TAVMedia to render video

【Android】

//In Android, implement the TAVTextureView control inherited from the TextureView control, create a TAVSurface through the SurfaceTexture in the TextureView, and render the picture on the TAVSurface. The implementation of TAVTextureView is as follows:

public class TAVTextureView extends TextureView implements TextureView.SurfaceTextureListener {

    private static final String TAG = "TAVTextureView";
    private final int fps = 20;
    private final int frameDuration = 1000 / fps;
    private boolean isAttachedToWindow = false;
    private TAVSurface mediaSurface;
    private TAVVideoReader videoReader;
    private TAVMovie media;
    private boolean isPlaying = false;

    public TAVTextureView(Context context) {
        this(context, null);
    }

    public TAVTextureView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOpaque(false);
        setSurfaceTextureListener(this);
        setKeepScreenOn(true);
    }

    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        if (mediaSurface != null) {
            mediaSurface.release();
            mediaSurface = null;
        }
        if (videoReader != null) {
            videoReader.release();
            videoReader = null;
        }
        // 通过 SurfaceTexture 创建 TAVSurface 
        mediaSurface = TAVSurface.FromSurfaceTexture(surface);
        if (!isPlaying) {
            play();
        }
    }

    public void setMedia(TAVMovie media) {
        this.media = media;
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        if (isPlaying) {
            stop();
        }
        if (mediaSurface != null) {
            mediaSurface.release();
            mediaSurface = null;
            videoReader = null;
        }
        if (videoReader != null) {
            videoReader.release();
            videoReader = null;
        }
        if (!isPlaying) {
            stop();
        }
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
    }

    @Override
    protected void onAttachedToWindow() {
        isAttachedToWindow = true;
        super.onAttachedToWindow();
    }

    @Override
    protected void onDetachedFromWindow() {
        isAttachedToWindow = false;
        super.onDetachedFromWindow();
    }

    public boolean isPlaying() {
        return isPlaying;
    }

    public void stop() {
        isPlaying = false;
    }

    public void play() {
        if (!isAttachedToWindow) {
            return;
        }
        isPlaying = true;
        new Thread(new RenderRunnable(), "tav_demo_play_thread").start();
        new Thread(new AudioRenderRunnable(), "tav_demo_play_audio_thread").start();
    }

    private class RenderRunnable implements Runnable {

        @Override
        public void run() {
            if (mediaSurface == null) {
                return;
            }
            videoReader = TAVVideoReader.Make(media, fps);
            if (videoReader == null) {
                return;
            }
            videoReader.setSurface(mediaSurface);
            runLoop(videoReader);
        }

        private void runLoop(TAVVideoReader videoReader) {
            while (isPlaying()) {
                long startTime = System.currentTimeMillis();
                videoReader.readNextFrame();
                long timeCons = System.currentTimeMillis() - startTime;
                Log.d(TAG, "video read: timeCons = " + timeCons);
                if (timeCons < frameDuration) {
                    trySleep(frameDuration - timeCons);
                }
            }
        }

        private void trySleep(long millis) {
            try {
                Thread.sleep(millis);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private class AudioRenderRunnable implements Runnable {

        @Override
        public void run() {

            TAVAudioReader audioReader = TAVAudioReader.Make(media);
            AudioTrackWrapper audioTrackWrapper = new AudioTrackWrapper(44100, 2);
            audioTrackWrapper.setVolume(1);
            // Start
            runLoop(audioReader, audioTrackWrapper);
            // Finish
            audioTrackWrapper.release();
        }

        private void runLoop(TAVAudioReader audioReader, AudioTrackWrapper audioTrackWrapper) {
            if (audioReader == null || audioTrackWrapper == null) {
                return;
            }
            while (isPlaying()) {
                long startTime = System.currentTimeMillis();
                TAVAudioFrame frame = audioReader.readNextFrame();
                audioTrackWrapper.writeData(frame.data, 0, (int) frame.length);
                long timeCons = System.currentTimeMillis() - startTime;
                Log.i(TAG, "audio read: timeCons = " + timeCons + ", timestamp = " + frame.timestamp + ", duration = "
                        + frame.duration);

                long frameDurationMs = frame.duration / 1000;
                if (timeCons < frameDurationMs) {
                    trySleep(frameDurationMs - timeCons);
                }
            }
        }

        private void trySleep(long millis) {
            try {
                Thread.sleep(millis);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//Build the rendering structure composition and perform rendering as follows:
// Create rendering structure
TAVComposition makeComposition(){
    long totalDuration = 3 * 1000 * 1000;
    int maxWidth = 0;

    TAVMovieAsset movieAsset = TAVMovieAsset.MakeFromPath(Utils.OUT_SAVE_DIR + "video-640x360.mp4");
    TAVMovie movie = TAVMovie.MakeFrom(movieAsset,0,totalDuration);

    movie.setStartTime(0);
    movie.setDuration(totalDuration);

    maxWidth = movieAsset.width();

    TAVImageAsset imageAsset = TAVImageAsset.MakeFromPath(Utils.OUT_SAVE_DIR + "hermione.jpg");
    TAVMovie imageMovie = TAVMovie.MakeFrom(imageAsset,0,totalDuration);
    imageMovie.setStartTime(0);
    imageMovie.setDuration(totalDuration);
    Matrix matrix = new Matrix();
    matrix.postTranslate(0,movieAsset.height() + 10);
    imageMovie.setMatrix(matrix);

    maxWidth = Math.max(maxWidth,imageAsset.width());

    TAVComposition composition = TAVComposition.Make(maxWidth,movieAsset.height() + imageAsset.height() + 10,0,totalDuration);
    composition.addClip(movie);
    composition.addClip(imageMovie);
    composition.setDuration(totalDuration);
    return composition;
}

// Perform rendering 
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);

TAVComposition composition = makeComposition();

Utils.fitToTarget(composition, displayMetrics.widthPixels, displayMetrics.heightPixels);

textureView.setMedia(composition);

【iOS】

//  Create render target
- (TAVSurface*)createSurfaceWithCALayer {
    // Create CAEAGLLayer and add it to the view
    self.glLayer = [CAEAGLLayer layer];
    self.glLayer.frame = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height);
    self.glLayer.drawableProperties = @{ kEAGLDrawablePropertyRetainedBacking: @NO, kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8 };
    [self.view.layer addLayer:self.glLayer];

    // Create TAVSuface based on layer to quickly display the screen
    return [TAVSurface FromLayer:self.glLayer];
}

+ (CVPixelBufferRef)createPixelBuffer:(NSInteger)width height:(NSInteger)height {
    if (width == 0 || height == 0) {
        return nil;
    }
    CFDictionaryRef empty = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFMutableDictionaryRef attrs
        = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFDictionarySetValue(attrs, kCVPixelBufferIOSurfacePropertiesKey, empty);
    CVPixelBufferRef pixelBuffer = nil;
    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_32BGRA, attrs, &pixelBuffer);
    CFRelease(attrs);
    CFRelease(empty);
    if (status != kCVReturnSuccess) {
        return nil;
    }
    CFAutorelease(pixelBuffer);
    return pixelBuffer;
}

- (TAVSurface*)createSurfaceWithPixelBuffer {
    // Create an off-screen surface through pixelBuffer, and subsequent content will be drawn to the pixelBuffer.
    // TAVMedia can be quickly embedded into the existing rendering chain of the business through pixelBuffer.
    CVPixelBufferRef pixelBuffer = [self createPixelBuffer:movie.width height:movie.height];
    return [TAVSurface FromCVPixelBuffer:pixelBuffer];
}

// Create rendering structure
- (TAVMovie *)makeMovie {
    // TAVMedia time data is in microseconds, totalDuration is actually expressed as 3 seconds in TAVMedia
    NSInteger totalDuration = 3 * 1000 * 1000;
    // Construct the video structure that needs to be played
    NSString *movieFilePath = [[NSBundle mainBundle] pathForResource:@"video-640x360" ofType:@"mp4"];
    // asset is the required resource data
    TAVMovieAsset *movieAsset = [TAVMovieAsset MakeFromPath:movieFilePath];
    // movie inherits from clip, which represents a fragment of the asset and is used for playback in VideoReader/AudioReader
    // startTime in MakeFrom represents the start time of the movie in the asset, and duration represents the interception time of the movie in the asset
    // The startTime and duration during Make construction currently do not provide modification interfaces
    TAVMovie *movie = [TAVMovie MakeFrom:movieAsset startTime:0 duration:totalDuration];
    // Different from the Make function, setStartTime represents the start time in the parent composition container, and no longer represents the start time of the asset
    [movie setStartTime:0];
    // Different from the Make function, setDuration represents the duration in the parent composition container, and no longer represents the interception duration of the asset
    [movie setDuration:totalDuration];
    return movie;
}

// Build the render controller
- (void)setupVideoReader {
    self.movie = [self makeMovie];
    // Construct a videoReader to control video playback. The frameRate can be set according to business needs
    self.videoReader = [TAVVideoReader MakeFrom:self.movie frameRate:60];
    // Set the scaling mode so that the video is centered on the surface. Note: ScaleMode will overwrite the Movie matrix
    [self.videoReader setScaleMode:TAVScaleModeLetterBox];
    // Construct a surface to provide display targets for videoReader
    // If you want to embed it into an existing rendering chain, you can use [self createSurfaceWithPixelBuffer]
    self.surface = [self createSurfaceWithCALayer];
    [self.videoReader setSurface:self.surface];
    
    // Draw the 0th second
    [self displayFirstFrame];
}

- (void)displayFirstFrame {
    // Render the 0th second screen
    // seek to 0th second
    [self.videoReader seekTo:0];
    // When seekTo is called, readNextFrame will render the current frame data of seekTo
    // When seekTo is not called, each readNextFrame will cause the time inside the videoReader to increase automatically according to the frame rate.
    // After readNextFrame is called, the content will be drawn to the surface
    [self.videoReader readNextFrame];
}

Use TAVMedia SDK to obtain audio data

【Android】

TAVAudioReader audioReader = TAVAudioReader.Make(media);
TAVAudioFrame frame = audioReader.readNextFrame();

【iOS】

// Build audio controller
- (void)setupAudioReader {
    // Create an audio controller based on movie. The sampling rate, number of samples, and number of channels can be set according to business needs
    self.audioReader = [TAVAudioReader MakeFrom:self.movie sampleRate:44100 sampleCount:1024 channels:1];
    
    // Get the pcm data of the 0th second
    [self.audioReader seekTo:0]; //seek to the 0th second
    // When seekTo is called, readNextFrame will render the current frame data of seekTo
    // When seekTo is not called, each readNextFrame will obtain the next data block according to the audio configuration
    TAVAudioFrame *frame = [self.audioReader readNextFrame];
    frame.timestamp // Start time of audio data (unit us)
    frame.duration // Duration of audio data (unit us)
    frame.data // pcm data of audio data
}

TAVMedia Hierarchy

For TAVMedia SDK, TAVVideoReader, TAVAudioReader and TAVSurface are relatively simple and their usage methods are relatively fixed. Please refer to the basic functions for specific usage methods.

For businesses, modifying the rendering structure TAVClip is a complex editing operation. TAVClip is a segment of rendered content, which can be of specific types such as video, audio, images, or special effects. Usually, editing scenes requires processing multiple TAVClips, and the relationship between these TAVClips is the problem solved by the TAVMedia hierarchy.

Related class description

Class name Function
TAVClip Rendering structure base class, all rendering structures inherit from TAVClip
TAVComposition TAVComposition is a subclass of TAVClip, representing a collection of a series of TAVClips

Construct rendering structure

In TAVMedia SDK, TAVComposition is used to solve scenes where special effects need to span multiple TAVClips. For example, atmosphere effects and LUT effects need to be applied to all videos. At the same time, these special effects can only add one input, and cannot use all videos as inputs. At this time, all videos can be combined into a TAVComposition, and TAVComposition can be used as the input of special effects to meet the requirements. A constructed rendering structure usually looks like the following figure:
img

When constructing the rendering structure, a TAVClip cannot exist in multiple TAVCompositions at the same time. If a TAVClip is added to multiple TAVCompositions at the same time, the TAVClip will first be removed from the previous TAVComposition and then added to the new TAVComposition. At the same time, in order to prevent the TAVMedia hierarchy from becoming a ring, TAVComposition cannot add a child node to contain its own TAVComposition.

As shown in the figure below, it is disassembled using commonly used orbit scenes. Each track in the orbit scene corresponds to TAVMedia and is a TAVComposition.

The main track is equivalent to the bottom TAVComposition as the input of other effect tracks. When adding an effects track, such as a LUT effects track, this behavior combines its own effects TAVClip with the input into a new TAVComposition. Tracks that do not require input, such as audio tracks or picture-in-picture tracks, can be added to the final rendered TAVComposition.
img

TAVMedia display control

This section describes how to use the TAVMedia SDK to control position and transparency.

Introduction to related classes

Class name Function
TAVAsset Material resource class, used to represent basic material resources
TAVMovie TAVMovie is used to create rendering data of different time periods based on the material TAVAsset, and can also set location information
TAVComposition TAVComposition inherits from TAVMovie, and TAVComposition represents the composition of a series of TAVClips. The operation of TAVComposition will be applied to all child TAVClips

Use matrix, cropRect and alpha to control display behavior

Except for TAVMovie, other TAVClips usually do not have location information. In TAVMovie, businesses can control the display behavior of TAVMovie by setting matrix, cropRect and alpha.

Matrix

Matrix is the change matrix of Movie in Composition, used to control the position of Movie. Matrix defaults to a standard matrix. The relative (0,0) point of the Matrix is the upper left corner of the parent matrix. The specific coordinate system is shown in the figure below:
img

CropRect

CropRect is the cropping area of the Movie relative to itself. cropRect is only related to the width and height of the Movie itself and has nothing to do with other Clips. During the rendering process, Movie will first use Matrix to calculate its position, and then use cropRect to crop other parts.
img

Example

Example1

Take the middle 1/4 part and display it in its original position. The code is as follows:

【Android】

TAVMovie movieWithCropRectAndMatrix = TAVMovie.MakeFrom(asset, 0, totalDuration);
movieWithCropRectAndMatrix.setDuration(totalDuration);

Matrix matrix = new Matrix();
matrix.postTranslate((float)-asset.width() / 2,(float)-asset.height() / 2);
matrix.postScale(2.0f,2.0f);
matrix.postTranslate((float) asset.width() /2,(float) asset.height() / 2);

RectF cropRect = new RectF((float)asset.width() / 4,(float)asset.height() / 4,(float)asset.width() / 2,(float)asset.height() / 2);
movieWithCropRectAndMatrix.setCropRect(cropRect);
movieWithCropRectAndMatrix.setMatrix(matrix);

【iOS】

// movie displays part of the screen through CropRect + matrix and moves it to the correct position
// Movie will apply matrix first and then cropRect. Matrix is the position relative to the parent composition, and cropRect is the cropping area relative to its own content
TAVMovie *movieWithCropRectAndMatrix = [TAVMovie MakeFrom:asset startTime:0 duration:totalDuration];
[movieWithCropRectAndMatrix setDuration:totalDuration];
// Move the anchor point to 0,0
CGAffineTransform matrix = CGAffineTransformMakeTranslation(-asset.width / 2, -asset.height / 2);
// Stretch twice based on anchor point
matrix = CGAffineTransformConcat(matrix, CGAffineTransformMakeScale(2.0, 2.0));
// Restore anchor point displacement
matrix = CGAffineTransformConcat(matrix, CGAffineTransformMakeTranslation(asset.width / 2, asset.height / 2));
// After cropping, only the middle part remains
CGRect cropRect = CGRectMake(asset.width / 4, asset.height / 4, asset.width / 2, asset.height / 2);
[movieWithCropRectAndMatrix setCropRect:cropRect];
[movieWithCropRectAndMatrix setMatrix:matrix];

Example 2

You can control the transparency of a Movie by setting its opacity. The sample code is as follows:

Description:

The value range is 0.0 - 1.0. 0 is completely transparent, 1 is completely opaque.

【Android】

TAVMovie movieWithCropRectAndMatrix = TAVMovie.MakeFrom(asset, 0, totalDuration);
// Set upopacity
movieWithOpacity.setOpacity(0.5);

【iOS】

TAVMovie *movieWithOpacity = [TAVMovie MakeFrom:asset startTime:0 duration:totalDuration];
// Set the opacity directly through the opacity attribute
movieWithOpacity.opacity = 0.5;

TAVMedia time control

Introduction to related classes

Class name Function
TAVClip Base class for rendering structures, used to control the timeline
TAVComposition TAVComposition consists of a series of TAVClips, and operations on TAVComposition will be applied to all child TAVClips
TAVAsset Material resource class, used to represent basic material resources
TAVMovie TAVMovie is used to create rendering data of different time periods based on the material TAVAsset

Arrange the timeline through TAVClip and TAVComposition

In the TAVMedia SDK, timing control occurs through the nesting of TAVClip and TAVComposition.

  • TAVClip is the base class of the rendering structure and has two basic attributes: startTime and duration. startTime determines the time point when TAVClip appears in TAVComposition, and duration determines how long TAVClip appears in TAVComposition.

  • TAVComposition is a collection of TAVClips. The time of the TAVClip inside a TAVComposition is relative to the TAVComposition. TAVComposition can set ContentStartTime and ContentDuration when constructing, which is used to clip the collection timeline. At the same time, TAVComposition inherits from TAVClip, and startTime and duration can also be set and added to other TAVComposition.

Example

Assume that a TAVClip that appears in 1s and displays for 4s is added to TAVComposition, and the first 2s of TAVComposition are skipped. The specific situation is as shown in the figure below: The sample code is as follows:
img

【Android】

TAVClip clip = new TAVClip();
clip.setStartTime( 1 * 1000 * 1000);
clip.setDuration(4 * 1000 * 1000);

TAVComposition composition =  TAVComposition.Make(720,1280, 2 * 1000 * 1000, 10 * 1000 * 1000);
composition.setDuration(10 * 1000 * 1000);
composition.addClip(clip);

【iOS】

// createClip represents the previous behavior of creating clip
TAVClip * clip = [self createClip];
// Set the start time of Clip to 1000000us
clip.startTime = 1 * 1000 * 1000;
// Set the duration of Clip to 4000000us
clip.duration = 4 * 1000 * 1000;
// Create a Composition with a resolution of 720*1280
// The startTime during make corresponds to CompositionContentStartTime
// The duration during make corresponds to CompositionContentDuration
TAVComposition * composition = [TAVComposition Make:720 height:1280 startTime:2 * 1000 * 1000 duration:10 * 1000 * 1000];
// The default startTime of clip is 0. You don’t need to set composition.startTime = 0;
// Set compositionDuration to 10 * 1000 * 1000
composition.duration = 10 * 1000 * 1000;
// Add clip to composition
[composition addClip:clip];

Crop resource clips through TAVMovie

In addition to TAVComposition can crop its own content, TAVMovie can also crop the material TAVAsset.

TAVAsset is a material resource class, usually created based on Data or Path, and represents image, video, and audio resources. TAVAsset cannot be added directly to the rendering structure, and the rendering structure corresponding to the resource needs to be created through TAVMovie.

TAVMovie represents a clip in a TAVAsset. ContentStartTime and ContentDuration can be set during construction to determine the position of TAVMovie in TAVAsset. TAVMovie also inherits from TAVClip and can be used in TAVMovie to control the position of clips in TAVComposition.

Example

Construct a scene that intercepts 1 to 4 seconds of video resources. The sample code is as follows:

【Android】

String path = Utils.OUT_SAVE_DIR + "video-640x360.mp4";
TAVMovieAsset asset = TAVMovieAsset.MakeFromPath(path);
TAVMovie movie = TAVMovie.MakeFrom(asset, 1 * 1000 * 1000, 4 * 1000 * 1000);
movie.setStartTime(0);
movie.setDuration(4 * 1000 * 1000);

【iOS】

NSString *movieFilePath = [[NSBundle mainBundle] pathForResource:@"video-640x360" ofType:@"mp4"];
// Construct resource asset based on path
TAVAsset* asset = [TAVMovieAsset MakeFromPath:movieFilePath];
// Build a movie based on resources and intercept 1~4s of the resources
TAVMovie *movie = [TAVMovie MakeFrom:asset startTime:1 * 1000 * 1000 duration:4 * 1000 * 1000];
// ---- movie construction completed ----

// Note: After constructing the movie, you also need to set startTime and duration to define the position in the composition time
// Different from the Make function, setStartTime represents the starting time in the parent composition container, and no longer represents the starting time of the asset
movie.startTime = 0;
// Different from the Make function, setDuration represents the duration in the parent composition container, and no longer represents the interception duration of the asset
movie.duration = 4 * 1000 * 1000;

Variable speed

When TAVMovie's contentDuration does not match its own duration, TAVMovie will change speed. TAVComposition is similar to TAVMovie. When the ContentDuration and duration of TAVComposition are inconsistent, the content of TAVComposition will also change speed.

Example

Play a 10s video at double speed. The example code is as follows:

【Android】

// Change speed directly through Movie
long totalDuration = 10 * 1000 * 1000;
String path = Utils.OUT_SAVE_DIR + "video-640x360.mp4";
TAVMovieAsset asset = TAVMovieAsset.MakeFromPath(path);
TAVMovie movie = TAVMovie.MakeFrom(asset, 0, totalDuration);
movie.setStartTime(0);
movie.setDuration(totalDuration / 2.0);

// Change speed through Composition
String path = Utils.OUT_SAVE_DIR + "video-640x360.mp4";
TAVMovieAsset asset = TAVMovieAsset.MakeFromPath(path);
TAVMovie movie = TAVMovie.MakeFrom(asset, 0, totalDuration);
movie.setStartTime(0);
movie.setDuration(totalDuration);
TAVComposition composition = TAVComposition.Make(640, 360, 0, totalDuration);
composition.setStartTime(0);
composition.setDuration(totalDuration / 2.0);

【iOS】

// Change speed directly through Movie
NSInteger totalDuration = 10 * 1000 * 1000;
NSString *movieFilePath = [[NSBundle mainBundle] pathForResource:@"video-640x360" ofType:@"mp4"];
TAVMovieAsset *asset = [TAVMovieAsset MakeFromPath:movieFilePath];
TAVMovie *movie = [TAVMovie MakeFrom:asset startTime:0 duration:totalDuration];
[movie setStartTime:0];
[movie setDuration:totalDuration / 2.0];

// Change speed through Composition
NSString *movieFilePath = [[NSBundle mainBundle] pathForResource:@"video-640x360" ofType:@"mp4"];
TAVMovieAsset *asset = [TAVMovieAsset MakeFromPath:movieFilePath];
TAVMovie *movie = [TAVMovie MakeFrom:asset startTime:0 duration:totalDuration];
[movie setStartTime:0];
[movie setDuration:totalDuration];
TAVComposition* composition = [TAVComposition Make:640 height:360 startTime:0 duration:totalDuration];
[composition setStartTime:0];
[composition setDuration:totalDuration / 2.0];

Freeze

Similar to variable speed, when contentDuration = 0 or approximately 0, the TAVmovie or TAVcomposition screen will be fixed.

示例

Take the content of the first second of the video and freeze it for 10 seconds. The sample code is as follows:

【Android】

// Freeze frame through Movie
long totalDuration = 10 * 1000 * 1000;
String path = Utils.OUT_SAVE_DIR + "video-640x360.mp4";
TAVMovieAsset asset = TAVMovieAsset.MakeFromPath(path);
TAVMovie movie = TAVMovie.MakeFrom(asset, 1 * 1000 * 1000, 0);
movie.setStartTime(0);
movie.setDuration(totalDuration);

// Freeze frame through Composition
String path = Utils.OUT_SAVE_DIR + "video-640x360.mp4";
TAVMovieAsset asset = TAVMovieAsset.MakeFromPath(path);
TAVMovie movie = TAVMovie.MakeFrom(asset, 0, totalDuration);
movie.setStartTime(0);
movie.setDuration(totalDuration);
TAVComposition composition = TAVComposition.Make(640, 360, 1 * 1000 * 1000, totalDuration);
composition.setStartTime(0);
composition.setDuration(totalDuration);

【iOS】

// Freeze frame through Movie
NSInteger totalDuration = 10 * 1000 * 1000;
NSString *movieFilePath = [[NSBundle mainBundle] pathForResource:@"video-640x360" ofType:@"mp4"];
TAVMovieAsset *asset = [TAVMovieAsset MakeFromPath:movieFilePath];
TAVMovie *movie = [TAVMovie MakeFrom:asset startTime:1 * 1000 * 1000 duration:0];
[movie setStartTime:0];
[movie setDuration:totalDuration];

// Freeze frame through Composition
NSString *movieFilePath = [[NSBundle mainBundle] pathForResource:@"video-640x360" ofType:@"mp4"];
TAVMovieAsset *asset = [TAVMovieAsset MakeFromPath:movieFilePath];
TAVMovie *movie = [TAVMovie MakeFrom:asset startTime:0 duration:totalDuration];
[movie setStartTime:0];
[movie setDuration:totalDuration];
TAVComposition* composition = [TAVComposition Make:640 height:360 startTime:1 * 1000 * 1000 duration:0];
[composition setStartTime:0];
[composition setDuration:totalDuration];变速

TAVMedia audio playback

Introduction to related classes

Class name Function
TAVAudio Audio clips created through Asset
TAVAudioVolumeEffect Audio fade effect

TAVAudio

TAVAudio can create audio clips through Asset. At this time, Audio contains only audio data and no video data. Through TAVAudio, businesses can add background music and obtain audio from videos. The audio volume can be set through the volume interface, and the volume range is 0 - 1.0.

TAVAudioVolumeEffect

TAVAudioVolumeEffect is TAVMedia's own audio volume control effect, which can quickly fade in and out of Clips. The input to TAVAudioVolumeEffect can be TAVAudio or any Clip with audio.

Example

Add BGM and fade in/out. The code is as follows:

【Android】

long totalDuration = 10 * 1000 * 1000;
// Construct audio track
String path = Utils.OUT_SAVE_DIR + "hoaprox.mp3";
TAVAudioAsset asset = TAVAudioAsset.MakeFromPath(path);
TAVAudio audio = TAVAudio.MakeFrom(asset, 0, totalDuration);
audio.setDuration(totalDuration);
// Construct audio transformation effects
// Fade in for the first 3 seconds and fade out for the last 3 seconds
TAVAudioVolumeEffect effect = TAVAudioVolumeEffect.MakeFIFOEffect(audio,
        1.0f, 3 * 1000 * 1000, 3 * 1000 * 1000);
effect.setStartTime(0);
effect.setDuration(totalDuration);

【iOS】

// Construct audio track
NSString *audioPath = [[NSBundle mainBundle] pathForResource:@"hoaprox" ofType:@"mp3"];
TAVAudioAsset *asset = [TAVAudioAsset MakeFromPath:audioPath];
TAVAudio *audio = [TAVAudio MakeFrom:asset startTime:0 duration:totalDuration];
[audio setDuration:totalDuration];

// Construct audio transformation effects
// Fade in for the first 3 seconds and fade out for the last 3 seconds
TAVAudioVolumeEffect *effect = [TAVAudioVolumeEffect MakeFIFOEffect:audio maxVolume:1.0f fadeInDuration:3 * 1000 * 1000 fadeOutDuration:3 * 1000 * 1000];
// TAVAudioVolumeEffect can also be created based on a specific sound transformation array, allowing the business to customize the audio change curve
// + (instancetype)MakeVolumeEffect:(TAVClip *)clip volumeRampList:(NSArray<TAVKeyFrame *> *)volumeRampList;
// Note: The time of volumeRampList is relative to Effect. If the positions of Effect and Clip on the timeline are inconsistent, the effect may not be as expected

// Note that startTime and duration need to be set here to ensure that they appear at the same time as the input
[effect setStartTime:audio.startTime];
[effect setDuration:audio.durat

TAVMedia add effects

TAVMedia SDK uses TAVEffect to add various effects to TAVClip, including advanced functions such as hue and saturation adjustment, lut transformation, position transformation, transitions, and PAG filter effects.

Introduction to related classes

Class name Function
TAVClip Basic rendering structure
TAVEffect Inherited from TAVClip as the effect base class
TAVComposition TAVComposition consists of a series of TAVClips, which control the rendering process during rendering

Rendering process in TAVComposition

Usually, the rendering order of TAVComposition is in the order of addition. As shown in the following figure, TAVComposition has added TAVClip0, TAVClip1, and TAVClip2 in sequence.

  • At Time1, all three TAVClips are displayed, and Composition will draw TAVClip0 > TAVClip1 > TAVClip2 in sequence.

  • At Time0, since TAVClip1 cannot be displayed, the drawing order is TAVClip0 > TAVClip2.

  • At Time2, the drawing order is TAVClip1
    img

After adding TAVEffect, TAVEffect's InputClip will be rendered when TAVEffect needs it. As shown in the figure below, TAVClip2 is a TAVEffect, and TAVClip1 is the Input of TAVClip.

  • At Time0, the drawing order is TAVClip0 > TAVEffect. TAVEffect will try to obtain the image of TAVClip1. However, since TAVClip1 is not displayed, TAVEffect will treat the Input as not existing.

  • At Time1, the drawing order is TAVClip0 > TAVEffect. At this time, although TAVClip1 is within the display range, it will not be actively drawn. TAVEffect will obtain the image of TAVClip1 for processing and then draw it.

  • At Time2, the drawing order is TAVClip1, and TAVEffect is not displayed. TAVClip1 is not affected by Effect and is drawn directly to the canvas.
    img

Effects supported in TAVMedia

TAVTransformEffect

Enter the number Function Precautions
1 Support dynamically moving the position and transparency of Input based on time Parameter Property timeline is a relative Effect, and the coordinates corresponding to the anchor point are absolute coordinates.

Example

The effect of rotating 90 degrees over time. The code is as follows:

【Android】

TAVTransform2D transform2D = new TAVTransform2D();
TAVKeyframe[] keyframes = {
        TAVKeyframe.MakeLinear(0,1 * 1000 * 1000,0,90)
};
TAVAnimatableProperty rotation = new TAVAnimatableProperty(keyframes);
transform2D.rotation = rotation;

TAVTransformEffect effect = TAVTransformEffect.Make(transform2D);
effect.setStartTime(1 * 1000 * 1000);
effect.setDuration(1 * 1000 * 1000);
effect.addInput(inputClip);
composition.addClip(effect);

【iOS】

TAVTransform2D* transform2D = [TAVTransform2D new];
// Values that do not change over time can be directly used with TAVProperties
// exmaple:
// TAVProperty* value = [TAVProperty new];
// value.value = @(90);

// Construct a rotation Property that dynamically transforms over time
TAVAnimatableProperty* rotation = [TAVAnimatableProperty new];
// Create a linear rotation animation from 0 to 90 degrees, the animation lasts for 1 second
rotation.keyFrames = [TAVKeyFrame MakeLinear:0 endTime:1 * 1000 * 1000 startValue:@(0) endValue:@(90)];
transform2D.rotation = rotation;
// transform2D has other parameters, please refer to TAVTransform2D.h for details
// Create effect based on transform2D
TAVTransformEffect* effect = [TAVTransformEffect Make:transform2D];
// The effect starts from 1s, and the rotation animation will start from 2s
[effect setStartTime:1 * 1000 * 1000];
[effect setDuration:1 * 1000 * 1000];
// Add clip as the input of the effect. inputClip is a business-defined clip and is not created here
[effect addInput:inputClip];
// Add effect to composition. Composition is a business-defined composition and is not created here
[composition addClip:effect];

TAVColorTuningEffect

Enter the number Function Precautions
1 Modify the Input color (including a series of color changes such as color temperature, hue, saturation, etc.) The parameter Property timeline is relative to Effect, and the value range of the property is [-50, 50].

Example

The effect of changing saturation from -50 to 50 over time. The code is as follows:

【Android】

TAVColorTuning colorTuning = new TAVColorTuning();
TAVKeyframe[] keyframes = {
        TAVKeyframe.MakeLinear(0,1 * 1000 * 1000,-50,50)
};
TAVAnimatableProperty saturation = new TAVAnimatableProperty(keyframes);
colorTuning.saturation = saturation;

TAVColorTuningEffect effect = TAVColorTuningEffect.Make(colorTuning);
effect.setStartTime(1 * 1000 * 1000);
effect.setDuration(1 * 1000 * 1000);         
effect.addInput(inputClip);
composition.addClip(effect);

【iOS】

// The attributes in colorTuning have a value range of [-50,50]
TAVColorTuning* colorTuning = [TAVColorTuning new];

// Construct a saturation property that dynamically changes over time
TAVAnimatableProperty* saturation = [TAVAnimatableProperty new];
// Create a linear animation from -50 to +50, the animation lasts for 1 second
saturation.keyFrames = [TAVKeyFrame MakeLinear:0 endTime:1 * 1000 * 1000 startValue:@(-50) endValue:@(50)];
colorTuning.saturation = saturation;
// TAVColorTuning has other parameters, please refer to TAVColorTuning.h for details
// Create effect based on TAVColorTuning
TAVColorTuningEffect* effect = [TAVColorTuningEffect Make:colorTuning];
// The effect starts from 1s, and the saturation animation will start from 2s
[effect setStartTime:1 * 1000 * 1000];
[effect setDuration:1 * 1000 * 1000];
// Add clip as the input of the effect. inputClip is a business-defined clip and is not created here
[effect addInput:inputClip];
// Add effect to composition. Composition is a business-defined composition and is not created here
[composition addClip:effect];

TAVLUTEffect

Enter the number Function Precautions
1 Apply LUT graph to Input The LUT intensity range is [0,1], and the LUT image format requirement is a 512*512 image.

Example

Use LUT and set the intensity to 0.5. The code is as follows:

【Android】

String path = Utils.OUT_SAVE_DIR + "lut1.png";
TAVLUTEffect lutEffect = TAVLUTEffect.MakeFromPath(path);
lutEffect.setStrength(0.5f);
lutEffect.setStartTime(0);
lutEffect.setDuration(1 * 1000 * 1000);
lutEffect.addInput(inputClip);
composition.addClip(lutEffect);

【iOS】

// Obtain the path of the LUT image
NSString* lutPath = [[NSBundle mainBundle] pathForResource:@"lut1" ofType:@"png"];
TAVLUTEffect* lutEffect = [TAVLUTEffect Make:lutPath];
// Set the intensity of lut to 0.5
[lutEffect setStrength:0.5];
// Set start time
[lutEffect setStartTime:0];
// The setting duration is 1s
[lutEffect setDuration:1 * 1000 * 1000];
// Add clip as the input of the effect. inputClip is a business-defined clip and is not created here
[lutEffect addInput:inputClip];
// Add effect to composition. Composition is a business-defined composition and is not created here
[composition addClip:lutEffect];

TAVChromaMattingEffect

Enter the number Function Precautions
1 Based on chromaticity matting Intensity is the intensity of the cutout, and the value range is [0,1]
- When the intensity is 0, there is no cutout, and when the intensity is 1, the chromaticity range is the largest.
- Shadow is the edge softening degree, and the range is [0,1]. The larger the shadow, the more natural the edge will be.
- selectedColor is the base color.

Example

Based on black cutout. The code is as follows:

【Android】

// Create TAVChromaMattingEffect directly
TAVChromaMattingConfig config = new TAVChromaMattingConfig();
config.intensity = 0.2f;
config.shadow = 0.5f;
config.selectedColor = Color.BLACK;
TAVChromaMattingEffect effect = TAVChromaMattingEffect.Make(config);
// Set start time
effect.setStartTime(0);
// The setting duration is 1s
effect.setDuration(1 * 1000 * 1000);
// Add clip as the input of the effect. inputClip is a business-defined clip and is not created here
effect.addInput(inputClip);
// Add effect to composition. Composition is a business-defined composition and is not created here
composition.addClip(effect);

【iOS】

// Create TAVChromaMattingEffect directly
TAVChromaMattingEffect* effect = [TAVChromaMattingEffect Make:0.2 shadow:0.5 selectedColor:UIColor.blackColor];
// Set start time
[effect setStartTime:0];
// The setting duration is 1s
[effect setDuration:1 * 1000 * 1000];
// Add clip as the input of the effect. inputClip is a business-defined clip and is not created here
[effect addInput:inputClip];
// Add effect to composition. Composition is a business-defined composition and is not created here
[composition addClip:effect];

TAVPAGEffect

Enter the number Function Precautions
Editable image layer smaller than PAG file Use the input as the replacement layer of the PAG effect to apply the PAG effect. The scope of use includes transitions, atmosphere effects, PAG templates, etc. PAG itself has width and height. If it is placed at different scales, the Matrix needs to be set according to requirements.

Example1

Use PAG atmosphere files. The code is as follows:

【Android】

String path = Utils.OUT_SAVE_DIR + "fw.pag";
// Create PAGEffect
TAVPAGEffect pagEffect = TAVPAGEffect.MakeFromPath(path);
// Add clip as the input of the effect. inputClip is a business-defined clip and is not created here
pagEffect.addInput(inputClip);
// numImgs represents the maximum number of replacement layers supported by PAG
int numImgs = pagEffect.numImages();
// PAG files may have multiple replacement layers. You can open the PAGViewer tool to view the specific properties of the file
// Set the first inputClip as the replacement data of the 0th picture layer in the PAG file. After the setting is completed, TAVMedia can superimpose the effect on it
// When the width and height of inputClip are inconsistent with PAGFile, you can use replace:inputIndex:scaleMode: here.
// Set the inputClip scaling mode. For specific behavior, please refer to TAVScaleMode.h
pagEffect.replaceImage(0,0);
// This can be set to any length according to requirements. In the example, it is set to the file length
pagEffect.setDuration(pagEffect.fileDuration());
// Add effect to composition. Composition is a business-defined composition and is not created here
composition.addClip(pagEffect);

【iOS】

// Obtain PAGfile path
NSString* fwPath = [[NSBundle mainBundle] pathForResource:@"fw" ofType:@"pag"];
// Create PAGEffect
TAVPAGEffect* pagEffect = [TAVPAGEffect Make:fwPath];
// Add clip as the input of the effect. inputClip is a business-defined clip and is not created here
[pagEffect addInput:inputClip];
// numImgs represents the maximum number of replacement layers supported by PAG
int numImgs = [pagEffect numImages];
// PAG files may have multiple replacement layers. You can open the PAGViewer tool to view the specific properties of the file
// Set the first inputClip as the replacement data of the 0th picture layer in the PAG file. After the setting is completed, TAVMedia can superimpose the effect on it
// When the width and height of inputClip are inconsistent with PAGFile, you can use replace:inputIndex:scaleMode: here.
// Set the inputClip scaling mode. For specific behavior, please refer to TAVScaleMode.h
[pagEffect replace:0 inputIndex:0];
// This can be set to any length according to requirements. In the example, it is set to the file length
[pagEffect setDuration:pagEffect.fileDuration];
// Add effect to composition. Composition is a business-defined composition and is not created here
[composition addClip:pagEffect];

Example2

Use PAG transition files for transitions. The code is as follows:

【Android】

int movieDuration = 3_000_000;
// Construct movie1
TAVMovieAsset asset1 = TAVMovieAsset.MakeFromPath(Utils.OUT_SAVE_DIR + "1.mp4");
TAVMovie movie1 = makeMovie(asset1, movieDuration, width, height);
movie1.setDuration(movieDuration);
// Construct movie2
TAVMovieAsset asset2 = TAVMovieAsset.MakeFromPath(Utils.OUT_SAVE_DIR + "2.mp4");
TAVMovie movie2 = makeMovie(asset2, movieDuration, width, height);
movie2.setDuration(movieDuration);
// Obtain the PAG transition file path
String transitionPath = Utils.OUT_SAVE_DIR + "zc.pag";
// Create PAGEffect
TAVPAGEffect pagEffect = TAVPAGEffect.MakeFromPath(transitionPath);
// numImgs represents the maximum number of replacement layers supported by PAG
// The transition effect has more than two numImages
int numImgs = pagEffect.numImages();
// Add movie1, movie2 as input to effect
pagEffect.addInput(movie1);
pagEffect.addInput(movie2);

// Set movie1 and movie2 respectively to replace the first replacement layer and the second replacement layer in the pagfile
// Which two replacement layers to replace need to be determined according to the PAG file. Usually input0 corresponds to editableIndex0 and input1 corresponds to editableIndex1
pagEffect.replaceImage(0,0);
pagEffect.replaceImage(1,1);
pagEffect.setDuration(pagEffect.fileDuration());

// This can be set to any length according to requirements. In the example, it is set to the file length
movie2.setStartTime(movie1.duration() - pagEffect.duration());
// Set the transition to start together with movie2
pagEffect.setStartTime(movie2.startTime());
// Add effect to composition. Composition is a business-defined composition and is not created here
composition.addClip(pagEffect);

【iOS】

// Construct two video clips movie1 and movie2
NSString *moviefilePath1 = [[NSBundle mainBundle] pathForResource:@"1" ofType:@"mp4"];
NSString *moviefilePath2 = [[NSBundle mainBundle] pathForResource:@"2" ofType:@"mp4"];
NSInteger movieDuration = 3 * 1000 * 1000;

// Construct movie1
movieAsset = [TAVMovieAsset MakeFromPath:moviefilePath1];
TAVMovie *movie1 = [TAVMovie MakeFrom:movieAsset startTime:0 duration:movieDuration];
[movie1 setDuration:movieDuration];

// Construct movie2
movieAsset = [TAVMovieAsset MakeFromPath:moviefilePath2];
TAVMovie *movie2 = [TAVMovie MakeFrom:movieAsset startTime:0 duration:movieDuration];
[movie2 setDuration:movieDuration];


// Obtain PAG transition file path
NSString* zcPath = [[NSBundle mainBundle] pathForResource:@"zc" ofType:@"pag"];
// Create PAGEffect
TAVPAGEffect* pagEffect = [TAVPAGEffect Make:zcPath];
// numImgs represents the maximum number of replacement layers supported by PAG
// The transition effect has more than two numImages
int numImgs = [pagEffect numImages];

// Add movie1, movie2 as input to effect
[pagEffect addInput:movie1];
[pagEffect addInput:movie2];


//  Set movie1 and movie2 respectively to replace the first replacement layer and the second replacement layer in the pagfile
// Which two replacement layers to replace need to be determined according to the PAG file. Usually input0 corresponds to editableIndex0 and input1 corresponds to editableIndex1
[pagEffect replace:0 inputIndex:0];
[pagEffect replace:1 inputIndex:1];

// This can be set to any length according to requirements. In the example, it is set to the file length
[pagEffect setDuration:pagEffect.fileDuration];

// Set the start duration of movie2 so that movie1 and movie2 have the overlapping duration of pagEffect.duration as the transition duration
movie2.startTime = movie1.duration - pagEffect.duration;

// Set the transition to start together with movie2
pagEffect.startTime = movie2.startTime;

// Add effect to composition. Composition is a business-defined composition and is not created here
[composition addClip:pagEffect];

TAVMedia Export

Introduction to related classes

Class name Function
TAVExport Export video
TAVMedia Rendering structure to be exported

TAVExport

TAVExport can directly export TAVMedia to the corresponding file path according to the set config parameters.

Example

【Android】

// Construct exported configuration
TAVExportConfig config = new Builder()
        .setVideoWidth(media.width()) // Configure the width of the exported video
        .setVideoHeight(media.height()) // Configure the height of the exported video
        .setOutFilePath(outputFilePath) // Configure exported file path
        .setUseHWEncoder(true) 
        .build();
// Enable asynchronous thread export, callback is the exported callback function
new Thread(() -> new TAVExport(media, config, callback).export()).start();

【iOS】

// Get the data structure that needs to be exported
TAVMedia *media = [self makeExportMovie];
// Construct export parameters
TAVExportConfig *config = [TAVExportConfig new];
// Configure the width and height of the exported video
config.videoWidth = movie.width;
config.videoHeight = movie.height;
// Configure exported file path
config.outPath = [self exportPath];
// delegate is the callback agent TAVMediaExportDelegate of the export function, which supports obtaining progress, receiving failure errors, and accepting completion events
self.exporter = [TAVExport MakeMediaExport:media config:config delegate:self];
// Start export
[self.exporter exportMedia];
← TAVMedia Quick accessFrom Lottie To PAG →
  • TAVMedia Authentication
  • Basic function
    • TAVClip
    • TAVSurface
    • TAVVideoReader
  • Use TAVMedia to render video
  • Use TAVMedia SDK to obtain audio data
  • TAVMedia Hierarchy
  • Related class description
  • Construct rendering structure
  • TAVMedia display control
  • Introduction to related classes
  • Use matrix, cropRect and alpha to control display behavior
    • Matrix
    • CropRect
  • Example
    • Example1
    • Example 2
  • TAVMedia time control
  • Introduction to related classes
  • Arrange the timeline through TAVClip and TAVComposition
  • Crop resource clips through TAVMovie
  • Variable speed
  • Freeze
  • TAVMedia audio playback
  • Introduction to related classes
    • TAVAudio
    • TAVAudioVolumeEffect
  • Example
  • TAVMedia add effects
  • Introduction to related classes
  • Rendering process in TAVComposition
  • Effects supported in TAVMedia
    • TAVTransformEffect
    • TAVColorTuningEffect
    • TAVLUTEffect
    • TAVChromaMattingEffect
    • TAVPAGEffect
  • TAVMedia Export
  • Introduction to related classes
    • TAVExport
  • Example
Address: Tencent Binhai Building, No. 33 Haitian Second Road, Nanshan District, Shenzhen, Guangdong Province, China.
TEL: 0755-86013388
QQ Group: 893379574
Copyright © 2018 - 2025 Tencent. All Rights Reserved.
Privacy Policy
公司地址:广东省深圳市南山区海天二路33号腾讯滨海大厦
联系电话:0755-86013388
QQ群:893379574
Copyright © 2018 - 2025 Tencent. All Rights Reserved.
隐私政策
Copyright © 2018 - 2025 Tencent. All Rights Reserved.
Address: Tencent Binhai Building, No. 33 Haitian Second Road, Nanshan District, Shenzhen, Guangdong Province, China.
TEL: 0755-86013388
QQ Group: 893379574
Privacy Policy
Copyright © 2018 - 2025 Tencent. All Rights Reserved.
公司地址:广东省深圳市南山区海天二路33号腾讯滨海大厦
联系电话:0755-86013388
QQ群:893379574
隐私政策