Export To Video
The PAG SDK is designed specifically for rendering effects. However, the Community Edition does not support audio and video-related content. If you need the capability to render exported video, we recommend utilizing the PAG Enterprise Edition. PAG Enterprise Edition (package name with movie suffix) includes the Movie function, allowing you to load a PAGComposition instance and export it as a video.
Use Flow
1、Build a PAGComposition instance
To create a PAGComposition instance, you can use either the load interface of PAGFile or the Make interface of PAGComposition. Once created, you can perform various operations such as editing text and replacing placeholder images. It's important to note that a single PAGComposition instance cannot be reused simultaneously in multiple rendering scenarios. For instance, you are not allowed to use the same PAGComposition for PAGView rendering, playing, and exporting videos simultaneously.
Code Examples
Android:
private static PAGComposition createComposition() {
// Build a new composition instance, and then replace the PAGImage of the compositionForPlayer in to the new instance
PAGFile composition = PAGFile.Load(PAG_FILE_PATH);
for (int i = 0; i < selectVideos.size() && i < composition.numImages(); i++) {
// Obtain the image or video to be replaced
PAGMovie movie = PAGMovie.MakeFromFile(selectVideos.get(i));
// Replace it in Composition
composition.replaceImage(i, movie);
}
return composition;
}
private void export() {
PAGComposition exportComposition = createComposition();
...
}
private void display() {
PAGComposition previewComposition = createComposition();
...
}
iOS:
- (PAGComposition*)createComposition {
NSString* pagPath = [[NSBundle mainBundle] pathForResource:fileName ofType:extension];
// Build Composition
PAGFile* file = [PAGFile Load:pagPath];
if (file == nil) {
return nil;
}
for (int i = 0; i < self.replaceMovies.count; i++) {
// Obtain the image or video to be replaced
NSString* moviePath = self.replaceMovies[i];
PAGMovie* movie = [PAGMovie MakeFromFile:moviePath];
// Replace it in Composition
[file replaceImage:i data:movie];
}
return file;
}
- (void)export {
PAGComposition* exportComposition = [self createComposition];
……
}
- (void)display {
PAGComposition* exportComposition = [self createComposition];
……
}
2、Build a PAGMovieExporter instance
PAGMovieExporter is the main class of export logic, and it requires the inclusion of PAGComposition instance, ExportConfig and Callback during building. Among them, ExportConfig is used to configure export parameters, such as resolution, output path and other parameters; Callback is used to asynchronously receive information callback. A more detailed explanation of the Callback will be provided later.
To ensure the prevention of memory leaks, it is important to note that the SDK will release associated resources upon the destruction of the PAGMovieExporter object. It is necessary for the user to retain the PAGMovieExporter object until the export task is completed in order to receive the callback as intended. Additionally, it is highly recommended to create a new instance of PAGMovieExporter for each export to minimize the occurrence of errors.
Currently, PAGMovieExporter only supports exporting mp4 and mov files in h264 format.
Code Examples
Andoird:
/**
* You need to retain a PAGExportSession instance to ensure that it is not released before the export task is completed
*/
private PAGExportSession exporter;
private void export(PAGComposition composition) {
PAGExportSession.Config config = new PAGExportSession.Config();
config.width = composition.width();
config.height = composition.height();
config.outputPath = "/sdcard/pag_commerce_demo/pag_export/output.mp4";
exporter = new PAGExportSession(pagFile, config, new MyCallback());
}
iOS:
// Create default export configuration
PAGExportConfig* config = [PAGExportConfig new];
// Set path
config.outputPath =
[[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject]
stringByAppendingString:@"/tmp.mp4"];
config.width = exportComposition.width;
config.height = exportComposition.height;
config.frameRate = exportComposition.frameRate;
PAGMovieExporter* exporter = [PAGMovieExporter Make:exportComposition config:config callback:self];
self.exporter = exporter;
3、 Start /cancel the export
Calling the start interface will initiate the export task. However, it's important to note that this process involves initializing the encoder, which may take some time. If there is a concern that the start time could potentially cause the main thread to become unresponsive, a possible solution is to execute the start interface on an asynchronous thread.
Code Examples
Android:
exporter.start();
iOS:
[exporter start];
To cancel the export task while it is running, you can directly invoke the cancel method. Although the cancel method can return immediately, it will not halt the export task. Therefore, It is important to refrain from deleting the export file immediately. The export cannot be formally canceled until the Callback returns with the Canceled status.
Code Examples
Android:
// After calling the cancel method, you need to wait for the SDK to call back Canceled before performing operations such as file deletion
exporter.cancel();
iOS:
// 调用 cancel 方法后,需要等待 SDK 回调 Canceled 后,再进行文件删除等操作
[self.exporter cancel];
4、Callback monitoring
Export callback is used to asynchronously receive export progress and status changes.
Status descriptions:
- Exporting:The task is in progress. In this status, the SDK will call back the real-time progress through the Callback.onProgress interface.
- Failed:Export has failed, and the session ends.
- Canceled: The task is canceled, and the session ends.
- Complete: The export is completed, and the session ends. The exported video file can then be processed.
- UnKnow:Unknown status. It can be regarded as Failed. If so, please give us feedback.
Android:
private class MyCallback implements PAGExportSession.Callback {
@Override
public void onProgress(final float progress) {
Log.d(TAG, "onProgress() called with: progress = [" + progress + "]");
runOnUiThread(() -> dialog.setProgress((int) (progress * 100)));
}
@Override
public void onStatusChange(Status status, String[] msg) {
Log.d(TAG, "onStatusChange: status = [" + status + "], msg = [" + Arrays.toString(msg) + "]");
switch (status) {
case Exporting:
// do nothing
break;
case UnKnow:
case Failed:
case Canceled:
case Complete:
runOnUiThread(() -> {
// eexporter.release(), release memory in time. It cannot be called directly in onStatusChange because exporter holds the callback lifecycle
exporter.release();
exporter = null;
dialog.dismiss();
});
break;
}
}
}
iOS:
- (void)onProgress:(CGFloat)progress {
[self.exportProgressView setProgress:progress];
}
- (void)onStatusChange:(PAGExportStatus)status msgs:(NSArray<NSString*>*)msgs {
if (status == PAGExportStatusComplete || status == PAGExportStatusFailed ||
status == PAGExportStatusCanceled) {
// Remove the progress bar
[self.exportProgressView removeFromSuperview];
self.exportProgressView = nil;
self.exporter = nil;
}
if (status == PAGExportStatusComplete) {
UIAlertController* alert =
[UIAlertController alertControllerWithTitle:@"Export ended"
message:@""
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:nil]];
// Pop up prompt box
[self presentViewController:alert animated:true completion:nil];
}
}