iOS-读取/写入mp4视频的XMP元数据

iOS-读取/写入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

我需要在mp4容器中读取并注入XMP元数据

我知道这在android上是可能的,有“mp4parser”库,但我找不到与iOS相同的

对于读取部分,是否有可能读取摄像机卷中的每个镜头,以快速检查其360 XMP元数据

在本文中,我尝试使用Adobe的XMP工具包。我在一个文件夹中有一个mp4视频,我想向其中注入一些360元数据

在注入元数据(我想它可以工作)之后,我将视频导出到摄影机卷,但看起来视频被转换为m4v,并且丢失了我编写的所有元数据。是预期的,还是我的代码错了

代码如下:

MetadataManager.mm

#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工具包。