iOS-读取/写入mp4视频的XMP元数据
我需要在mp4容器中读取并注入XMP元数据 我知道这在android上是可能的,有“mp4parser”库,但我找不到与iOS相同的 对于读取部分,是否有可能读取摄像机卷中的每个镜头,以快速检查其360 XMP元数据 在本文中,我尝试使用Adobe的XMP工具包。我在一个文件夹中有一个mp4视频,我想向其中注入一些360元数据 在注入元数据(我想它可以工作)之后,我将视频导出到摄影机卷,但看起来视频被转换为m4v,并且丢失了我编写的所有元数据。是预期的,还是我的代码错了 代码如下: MetadataManager.mmiOS-读取/写入mp4视频的XMP元数据,ios,objective-c,metadata,xmp,360-degrees,Ios,Objective C,Metadata,Xmp,360 Degrees,我需要在mp4容器中读取并注入XMP元数据 我知道这在android上是可能的,有“mp4parser”库,但我找不到与iOS相同的 对于读取部分,是否有可能读取摄像机卷中的每个镜头,以快速检查其360 XMP元数据 在本文中,我尝试使用Adobe的XMP工具包。我在一个文件夹中有一个mp4视频,我想向其中注入一些360元数据 在注入元数据(我想它可以工作)之后,我将视频导出到摄影机卷,但看起来视频被转换为m4v,并且丢失了我编写的所有元数据。是预期的,还是我的代码错了 代码如下: Metada
#import "MetadataManager.h"
#define IOS_ENV 1
#include <string>
#define TXMP_STRING_TYPE std::string
#define XMP_INCLUDE_XMPFILES 1
#include "XMP.incl_cpp"
#include "XMP.hpp"
#include <iostream>
#include <fstream>
using namespace std;
@implementation MetadataManager {
}
+ (void)write360Metadatas:(NSString *)filePath {
if (!SXMPMeta::Initialize())
exit(1);
if (!SXMPFiles::Initialize())
exit(1);
SXMPFiles myFile;
XMP_OptionBits opts = kXMPFiles_OpenForUpdate | kXMPFiles_OpenUseSmartHandler;
std::string status = "";
std::string filePathStd = std::string([filePath UTF8String]);
// First, try to open the file
bool ok = myFile.OpenFile(filePathStd, kXMP_UnknownFile, opts);
if( ! ok ){
status += "No smart handler available for " + filePathStd + "\n";
status += "Trying packet scanning.\n";
// Now try using packet scanning
opts = kXMPFiles_OpenForUpdate | kXMPFiles_OpenUsePacketScanning;
ok = myFile.OpenFile(filePathStd, kXMP_UnknownFile, opts);
}
if(ok){
SXMPMeta meta;
myFile.GetXMP( &meta );
displayPropertyValues(&meta);
injectMetadatas(&meta);
// Check we can put the XMP packet back into the file
if(myFile.CanPutXMP(meta))
{
// If so then update the file with the modified XMP
myFile.PutXMP(meta);
}
// Close the SXMPFile. This *must* be called. The XMP is not
// actually written and the disk file is not closed until this call is made.
myFile.CloseFile();
}
}
SXMPMeta createXMPFromRDF()
{
const char * rdf =
"<rdf:SphericalVideo xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'"
" xmlns:GSpherical='http://ns.google.com/videos/1.0/spherical/'>"
"<GSpherical:Spherical>true</GSpherical:Spherical>"
"<GSpherical:Stitched>true</GSpherical:Stitched>"
"<GSpherical:StitchingSoftware>Spherical Metadata Tool</GSpherical:StitchingSoftware>"
"<GSpherical:ProjectionType>equirectangular</GSpherical:ProjectionType>"
"</rdf:SphericalVideo>";
SXMPMeta meta;
// Loop over the rdf string and create the XMP object
// 10 characters at a time
int i;
for (i = 0; i < (long)strlen(rdf) - 10; i += 10 )
{
meta.ParseFromBuffer ( &rdf[i], 10, kXMP_ParseMoreBuffers );
}
// The last call has no kXMP_ParseMoreBuffers options, signifying
// this is the last input buffer
meta.ParseFromBuffer ( &rdf[i], (XMP_StringLen) strlen(rdf) - i );
return meta;
}
void injectMetadatas(SXMPMeta * meta)
{
// Add an item onto the dc:creator array
// Note the options used, kXMP_PropArrayIsOrdered, if the array does not exist it will be created
meta->AppendArrayItem(kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered, "Author Name", 0);
meta->AppendArrayItem(kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered, "Another Author Name", 0);
// Now update alt-text properties
meta->SetLocalizedText(kXMP_NS_DC, "title", "en", "en-US", "An English title");
meta->SetLocalizedText(kXMP_NS_DC, "title", "fr", "fr-FR", "Un titre Francais");
// Display the properties again to show changes
cout << "After update:" << endl;
displayPropertyValues(meta);
// Create a new XMP object from an RDF string
SXMPMeta rdfMeta = createXMPFromRDF();
// Append the newly created properties onto the original XMP object
// This will:
// a) Add ANY new TOP LEVEL properties in the source (rdfMeta) to the destination (meta)
// b) Replace any top level properties in the source with the matching properties from the destination
SXMPUtils::ApplyTemplate(meta, rdfMeta, kXMPTemplate_AddNewProperties | kXMPTemplate_ReplaceExistingProperties | kXMPTemplate_IncludeInternalProperties);
// Display the properties again to show changes
cout << "After Appending Properties:" << endl;
displayPropertyValues(meta);
}
void displayPropertyValues(SXMPMeta * meta)
{
// Read a simple property
string simpleValue; //Stores the value for the property
meta->GetProperty(kXMP_NS_XMP, "CreatorTool", &simpleValue, 0);
cout << "meta:CreatorTool = " << simpleValue << endl;
// Get the first and second element in the dc:creator array
string elementValue;
meta->GetArrayItem(kXMP_NS_DC, "creator", 1, &elementValue, 0);
if(elementValue != "")
{
cout << "dc:creator[1] = " << elementValue << endl;
meta->GetArrayItem(kXMP_NS_DC, "creator", 2, &elementValue, 0);
cout << "dc:creator[2] = " << elementValue << endl;
}
// Get the the entire dc:subject array
string propValue;
int arrSize = meta->CountArrayItems(kXMP_NS_DC, "subject");
for(int i = 1; i <= arrSize;i++)
{
meta->GetArrayItem(kXMP_NS_DC, "subject", i, &propValue, 0);
cout << "dc:subject[" << i << "] = " << propValue << endl;
}
// Get the dc:title for English and French
string itemValue;
string actualLang;
meta->GetLocalizedText(kXMP_NS_DC, "title", "en", "en-US", 0, &itemValue, 0);
cout << "dc:title in English = " << itemValue << endl;
meta->GetLocalizedText(kXMP_NS_DC, "title", "fr", "fr-FR", 0, &itemValue, 0);
cout << "dc:title in French = " << itemValue << endl;
// Get dc:MetadataDate
XMP_DateTime myDate;
if(meta->GetProperty_Date(kXMP_NS_XMP, "MetadataDate", &myDate, 0))
{
// Convert the date struct into a convenient string and display it
string myDateStr;
SXMPUtils::ConvertFromDate(myDate, &myDateStr);
cout << "meta:MetadataDate = " << myDateStr << endl;
}
cout << "----------------------------------------" << endl;
}
@end
#导入“MetadataManager.h”
#定义IOS_环境1
#包括
#定义TXMP_字符串_类型std::STRING
#定义XMP_INCLUDE_XMPFILES 1
#包括“XMP.incl_cpp”
#包括“XMP.hpp”
#包括
#包括
使用名称空间std;
@实现元数据管理器{
}
+(void)write360元数据:(NSString*)文件路径{
如果(!SXMPMeta::Initialize())
出口(1);
如果(!SXMPFiles::Initialize())
出口(1);
SXMPFiles-myFile;
XMP_OptionBits opts=kXMPFiles_OpenForUpdate | kXMPFiles_openuseMartHandler;
std::string status=“”;
std::string filePath std=std::string([filePath UTF8String]);
//首先,尝试打开该文件
bool ok=myFile.OpenFile(filePathStd、kXMP\u UnknownFile、opts);
如果(!ok){
状态+=“没有可用于“+filePathStd+”\n”的智能处理程序;
状态+=“正在尝试数据包扫描。\n”;
//现在尝试使用包扫描
opts=kXMPFiles_OpenForUpdate | kXMPFiles_OpenUsePacketScanning;
ok=myFile.OpenFile(filePathStd、kXMP\u UnknownFile、opts);
}
如果(确定){
SXMPMeta;
GetXMP(&meta);
显示属性值(&meta);
注入元数据(&meta);
//检查是否可以将XMP数据包放回文件中
if(myFile.CanPutXMP(meta))
{
//如果是这样,则使用修改后的XMP更新该文件
PutXMP(meta);
}
//关闭SXMP文件。此*必须*调用。XMP不可用
//实际上已写入,并且在进行此调用之前,磁盘文件不会关闭。
myFile.CloseFile();
}
}
SXMPMeta createXMPFromRDF()
{
常量字符*rdf=
""
“对”
“对”
“球形元数据工具”
“等矩形”
"";
SXMPMeta;
//循环rdf字符串并创建XMP对象
//一次10个字符
int i;
对于(i=0;i<(长)strlen(rdf)-10;i+=10)
{
meta.ParseFromBuffer(&rdf[i],10,kXMP_parsemorebuffer);
}
//最后一个调用没有kXMP_ParseMoreBuffers选项,表示
//这是最后一个输入缓冲区
meta.ParseFromBuffer(&rdf[i],(XMP_StringLen)strlen(rdf)-i);
返回元;
}
无效注入元数据(SXMPMeta*meta)
{
//将项目添加到dc:creator数组中
//请注意使用的选项,kXMP_PropArrayIsOrdered,如果数组不存在,将创建它
meta->AppendArrayItem(kXMP_NS_DC,“创建者”,kXMP_ProparrayOrdered,“作者姓名”,0);
meta->AppendArrayItem(kXMP_NS_DC,“创建者”,kXMP_ProparrayOrdered,“另一作者名”,0);
//现在更新alt文本属性
meta->SetLocalizedText(kXMP_NS_DC,“标题”、“en”、“en-US”、“英语标题”);
meta->SetLocalizedText(kXMP_NS_DC,“标题”、“fr”、“fr”、“Un titre Francais”);
//再次显示属性以显示更改
CUT我终于成功了,使用了“空间媒体”的C++端口,而不是Adobe的XMP工具包。