对于音视频媒体文件可以使用ffmpeg工具为音视频文件添加元数据,在HarmonyOS中可以使用三方库@ohos/mp4parser(V2.0.7)调用ffmpeg命令为音视频文件添加隐式标识。对于图片文件,通过modifyImageProperty接口将隐式元数据标识添加到图片的EXIF属性UserComment中。
const AV_AIGC_METADATA =
'{"Label":"value1","ContentProducer":"value2","ProduceID":"value3","ReservedCode1":"value4","ContentPropagator":"value5","PropagateID":"value6","ReservedCode2":"value7"}';
/**
*
* @param inputPath 输入音视频文件的沙箱路径(无隐式标识)
* @param outputPath 输出文件的沙箱路径(添加隐式标识)
* @param aigcMetadata 需要写入的AIGC文件元数据
* @returns
*/
async function addAIGC4AVFile(inputPath: string, outputPath: string, aigcMetadata: string): Promise<void> {
let callback: ICallBack = {
callBackResult: (code: number) => {
if (code === 0) {
console.info('Add aigc metadata succeed');
} else {
console.error('Add aigc metadata failed');
}
},
};
let cmd =
`ffmpeg -i ${inputPath} -metadata AIGC="${aigcMetadata}" -movflags use_metadata_tags -c copy ${outputPath} -y`;
MP4Parser.ffmpegCmd(cmd, callback);
} const IMAGE_AIGC_METADATA =
'{"AIGC":{"Label":"value1","ContentProducer":"value2","ProduceID":"value3","ReservedCode1":"value4","ContentPropagator":"value5","PropagateID":"value6","ReservedCode2":"value7"}}';
/**
*
* @param imageFilePath 输入图片文件的沙箱路径
* @param aigcMetadata 需要写入的AIGC文件元数据
*/
async function addAIGC4ImageFile(imageFilePath: string, aigcMetadata: string) {
let imageSource: image.ImageSource | undefined;
try {
imageSource = image.createImageSource(imageFilePath);
await imageSource.modifyImageProperty(image.PropertyKey.USER_COMMENT, aigcMetadata);
console.info(`Add aigc metadata succeed.`);
} catch (error) {
console.error(`Failed to invoke ImageSource.modifyImageProperty. Cause: ${JSON.stringify(error)}`);
} finally {
await imageSource?.release();
}
} 完整参考代码如下:
import { ICallBack, MP4Parser } from '@ohos/mp4parser';
import image from '@ohos.multimedia.image';
import { fileIo } from '@kit.CoreFileKit';
const AV_AIGC_METADATA =
'{"Label":"value1","ContentProducer":"value2","ProduceID":"value3","ReservedCode1":"value4","ContentPropagator":"value5","PropagateID":"value6","ReservedCode2":"value7"}';
/**
*
* @param inputPath 输入音视频文件的沙箱路径(无隐式标识)
* @param outputPath 输出文件的沙箱路径(添加隐式标识)
* @param aigcMetadata 需要写入的AIGC文件元数据
* @returns
*/
async function addAIGC4AVFile(inputPath: string, outputPath: string, aigcMetadata: string): Promise<void> {
let callback: ICallBack = {
callBackResult: (code: number) => {
if (code === 0) {
console.info('Add aigc metadata succeed');
} else {
console.error('Add aigc metadata failed');
}
},
};
let cmd =
`ffmpeg -i ${inputPath} -metadata AIGC="${aigcMetadata}" -movflags use_metadata_tags -c copy ${outputPath} -y`;
MP4Parser.ffmpegCmd(cmd, callback);
}
const IMAGE_AIGC_METADATA =
'{"AIGC":{"Label":"value1","ContentProducer":"value2","ProduceID":"value3","ReservedCode1":"value4","ContentPropagator":"value5","PropagateID":"value6","ReservedCode2":"value7"}}';
/**
*
* @param imageFilePath 输入图片文件的沙箱路径
* @param aigcMetadata 需要写入的AIGC文件元数据
*/
async function addAIGC4ImageFile(imageFilePath: string, aigcMetadata: string) {
let imageSource: image.ImageSource | undefined;
try {
imageSource = image.createImageSource(imageFilePath);
await imageSource.modifyImageProperty(image.PropertyKey.USER_COMMENT, aigcMetadata);
console.info(`Add aigc metadata succeed.`);
} catch (error) {
console.error(`Failed to invoke ImageSource.modifyImageProperty. Cause: ${JSON.stringify(error)}`);
} finally {
await imageSource?.release();
}
}
async function copyFile2Sandbox(rawFilePath: string, sandboxFilePath: string, context: Context) {
let sandboxFile: fileIo.File | undefined;
try {
sandboxFile = await fileIo.open(sandboxFilePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY);
let content = await context.resourceManager.getRawFileContent(rawFilePath);
await fileIo.write(sandboxFile.fd, content.buffer.slice(0));
} catch (error) {
console.error(`Failed to copy rawfile to sandbox. Cause: ${JSON.stringify(error)}`);
} finally {
if (sandboxFile) {
await fileIo.close(sandboxFile.fd);
}
}
}
@Entry
@Component
struct Index {
build() {
Column({ space: 20 }) {
Button('Video')
.padding(9)
.fontSize(30)
.onClick(async () => {
let context = this.getUIContext().getHostContext() as Context;
const inputPath = context.filesDir + '/input.mp4';
const outputPath = context.filesDir + '/output.mp4';
await copyFile2Sandbox('input.mp4', inputPath, context); // input.mp4为rawfile文件,根据实际使用的文件替换
await addAIGC4AVFile(inputPath, outputPath, AV_AIGC_METADATA);
});
Button('Audio')
.padding(9)
.fontSize(30)
.onClick(async () => {
let context = this.getUIContext().getHostContext() as Context;
const inputPath = context.filesDir + '/input.m4a';
const outputPath = context.filesDir + '/output.m4a';
await copyFile2Sandbox('input.m4a', inputPath, context); // input.m4a为rawfile文件,根据实际使用的文件替换
await addAIGC4AVFile(inputPath, outputPath, AV_AIGC_METADATA);
});
Button('Image')
.padding(9)
.fontSize(30)
.onClick(async () => {
let context = this.getUIContext().getHostContext() as Context;
const inputPath = context.filesDir + '/image.jpg';
await copyFile2Sandbox('image.jpg', inputPath, context); // image.jpg为rawfile文件,根据实际使用的文件替换
await addAIGC4ImageFile(inputPath, IMAGE_AIGC_METADATA);
});
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center);
}
}