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
任何幫助,將不勝感激,謝謝。