iOS — Чтение/запись метаданных XMP видео в формате mp4

Мне нужно прочитать и внедрить метаданные XMP в контейнер mp4.

Я знаю, что это возможно на 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 

Любая помощь будет оценена, спасибо.


person Xys    schedule 11.04.2017    source источник


Ответы (1)


Я, наконец, преуспел, используя порт С++ «spatial-media» вместо набора инструментов Adobe xmp.

spatial-media (репозиторий github)

person Xys    schedule 11.05.2017