iOS - Read/Write mp4 视频的 XMP 元数据

iOS - Read/Write XMP metadatas of mp4 videos

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

我知道 android 上的 "mp4parser" 库可以做到这一点,但我找不到 iOS 的等效项。

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

对于写作,我正在尝试使用 Adob​​e 的 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 

如有任何帮助,我们将不胜感激。

我终于成功了,使用 "spatial-media" 的 c++ 端口而不是 Adob​​e 的 xmp 工具包。

spatial-media (github repository)