Writing C# wrapper for method MP4TagsFetch in LibMP4v2
I will writing a class to reading MP4 files by using of the LibMP4v2 library, but C++ is not my favorite programing language.
The library contains following structure definitions:
/** Enumeration of 开发者_运维知识库possible MP4TagArtwork::type values. */
typedef enum MP4TagArtworkType_e
{
MP4_ART_UNDEFINED = 0,
MP4_ART_BMP = 1,
MP4_ART_GIF = 2,
MP4_ART_JPEG = 3,
MP4_ART_PNG = 4
} MP4TagArtworkType;
/** Data object representing a single piece of artwork. */
typedef struct MP4TagArtwork_s
{
void* data; /**< raw picture data */
uint32_t size; /**< data size in bytes */
MP4TagArtworkType type; /**< data type */
} MP4TagArtwork;
typedef struct MP4TagTrack_s
{
uint16_t index;
uint16_t total;
} MP4TagTrack;
typedef struct MP4TagDisk_s
{
uint16_t index;
uint16_t total;
} MP4TagDisk;
/** Tags <b>convenience</b> structure.
*
* This structure is used in the tags convenience API which allows for
* simplified retrieval and modification of the majority of known tags.
*
* This is a read-only structure and each tag is present if and only if the
* pointer is a <b>non-NULL</b> value. The actual data is backed by a hidden
* data cache which is only updated when the appropriate metadata <b>set</b>
* function is used, or if MP4TagsFetch() is invoked. Thus, if other API
* is used to manipulate relevent atom structure of the MP4 file, the user
* is responsible for re-fetching the data in this structure.
*/
typedef struct MP4Tags_s
{
void* __handle; /* internal use only */
const char* name;
const char* artist;
const char* albumArtist;
const char* album;
const char* grouping;
const char* composer;
const char* comments;
const char* genre;
const uint16_t* genreType;
const char* releaseDate;
const MP4TagTrack* track;
const MP4TagDisk* disk;
const uint16_t* tempo;
const uint8_t* compilation;
const char* tvShow;
const char* tvNetwork;
const char* tvEpisodeID;
const uint32_t* tvSeason;
const uint32_t* tvEpisode;
const char* description;
const char* longDescription;
const char* lyrics;
const char* sortName;
const char* sortArtist;
const char* sortAlbumArtist;
const char* sortAlbum;
const char* sortComposer;
const char* sortTVShow;
const MP4TagArtwork* artwork;
uint32_t artworkCount;
const char* copyright;
const char* encodingTool;
const char* encodedBy;
const char* purchaseDate;
const uint8_t* podcast;
const char* keywords; /* TODO: Needs testing */
const char* category;
const uint8_t* hdVideo;
const uint8_t* mediaType;
const uint8_t* contentRating;
const uint8_t* gapless;
const char* iTunesAccount;
const uint8_t* iTunesAccountType;
const uint32_t* iTunesCountry;
const uint32_t* contentID;
const uint32_t* artistID;
const uint64_t* playlistID;
const uint32_t* genreID;
const uint32_t* composerID;
const char* xid;
} MP4Tags;
My C# code is:
[DllImport("libmp4v2.dll", CharSet = CharSet.Ansi)]
private extern static IntPtr MP4Read([MarshalAs(UnmanagedType.LPStr)] string filePath);
[DllImport("libmp4v2.dll", CharSet = CharSet.Ansi)]
private extern static bool MP4TagsFetch(IntPtr tags, IntPtr fileHandle);
[DllImport("libmp4v2.dll", CharSet = CharSet.Ansi)]
private extern static IntPtr MP4TagsAlloc();
[DllImport("libmp4v2.dll", CharSet = CharSet.Ansi)]
private extern static IntPtr MP4FileInfo([MarshalAs(UnmanagedType.LPStr)] string filePath, uint trackId);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MP4TagTrack
{
public ushort Index;
public ushort Total;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MP4TagDisk
{
public ushort Index;
public ushort Total;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct CoverArtBox
{
public byte[] data; /**< raw picture data */
public uint size; /**< data size in bytes */
public MP4TagArtworkType type; /**< data type */
}
public enum MP4TagArtworkType
{
MP4_ART_UNDEFINED = 0,
MP4_ART_BMP = 1,
MP4_ART_GIF = 2,
MP4_ART_JPEG = 3,
MP4_ART_PNG = 4
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MP4Tags
{
public string Name;
public string Artist;
public string AlbumArtist;
public string Album;
public string Grouping;
public string Composer;
public string Comments;
public string Genre;
public byte[] GenreType;
public string ReleaseDate;
//public MP4TagTrack Track;
//public MP4TagDisk Disk;
public uint Tempo;
public uint Compilation;
public string TVShow;
public string TVEpisodeID;
public string TVNetwork;
public uint TVSeason;
public uint TVEpisode;
public string SortName;
public string SortArtist;
public string SortAlbumArtist;
public string SortAlbum;
public string SortComposer;
public string SortTVShow;
public string Description;
public string LongDescription;
public string Lyrics;
public CoverArtBox[] Artwork;
public uint ArtworkCount;
public string Copyright;
public string EncodingTool;
public string EncodedBy;
public string PurchaseDate;
public uint Podcast;
public string Keywords;
public string Category;
public uint HDVideo;
public uint MediaType;
public uint ContentRating;
public uint Gapless;
public string iTunesAccount;
public byte iTunesAccountType;
public uint iTunesCountry;
public uint ContentID;
public uint ArtistID;
public ulong PlaylistID;
public uint GenreID;
public uint ComposerID;
public string Xid;
public bool HasMetadata;
}
After calling of the external method MP4TagsFetch and converting the IntPtr into my structure are same fields filled with data of other fields:
IntPtr fileHandle = MP4Read(FilePath);
IntPtr tags = MP4TagsAlloc();
if (fileHandle != IntPtr.Zero && MP4TagsFetch(tags, fileHandle))
MP4Tags val = (MP4Tags)Marshal.PtrToStructure(tags, typeof(MP4Tags));
What's going wrong? Can anyone help me?
From the code provided and the description of the fields being off by one, I;d say it is because you did not include space for the handle.
void* __handle; /* internal use only */
Therefore, add this:
public struct MP4Tags
{
public IntPtr Handle; <-----------------------
public string Name;
public string Artist;
public string AlbumArtist;
etc
You should be able to make the field private to better protect it (since it is commented as internal-use only), though I did not test that out.
EDIT1
I don't think this is correct either:
public byte iTunesAccountType;
The C declaration was this:
const uint8_t* iTunesAccountType;
That is a pointer (4 bytes assuming 32 bit app).
I'd actually redefine your structure with IntPtr fields for any field that you think the marshalling in not working with, and iteratively bring them back in to help isoltae which one(s) are the culprit. The API looks like it is using entirey pointers to the data fields so every one is replaceable with IntPtr.
精彩评论