本文介绍了iOS-读/写mp4视频的XMP元数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I need to read and inject XMP metadatas in an mp4 container.

我知道这可以在带有"mp4parser"库的android上实现,但是我找不到适用于iOS的等效项.

I know this is possible on android with the "mp4parser" library, but I couldn't find an equivalent for iOS.

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

For the read part, is it possible to read every footage from the camera roll to inspect their 360 XMP metadatas quickly ?

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

For the writing, I'm trying to use Adobe's XMP toolkit. I have an mp4 video in a folder, and I want to inject into it some 360 metadatas.

注入元数据后(我想它可以正常工作),我将视频导出到相机胶卷,但看起来视频已转换为m4v,并且丢失了我编写的所有元数据.是预期的,还是我的代码错误?

After injecting the metadatas (I suppose it works), I export the video to the camera roll, but it looks like the video is converted to m4v and it lost every metadata I've written. Is it expected, or is my code wrong ?

这是代码:

MetadataManager.mm

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

感谢您的任何帮助.

推荐答案

我终于成功了,使用了空间媒体"的c ++端口而不是Adobe的xmp工具包.

I've finally succeeded, using the c++ port of "spatial-media" instead of Adobe's xmp toolkit.

空间媒体(github存储库)

这篇关于iOS-读/写mp4视频的XMP元数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-24 09:35