2017-04-11 64 views
0

我需要讀取並注入mp4容器中的XMP元數據。iOS - 讀取/寫入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 

任何幫助,將不勝感激,謝謝。

回答