Hi all, I wonder if anybody have tried to read/write WM file (wma,mp3) extended attributes, such as WM/Lyrics_Synchronised or WM/Picture. I tried to do this in VB.NET (many thanks to Robert Smith for help), using WMFSDKWrapper.dll from WMFSDK, and had no problems, reading/writing simple WM attributes of type WMT_ATTR_DATATYPE.WMT_TYPE_STRING, .WMT_TYPE_DWORD, .WMT_TYPE_QWORD or .WMT_TYPE_BOOL. I'm even able to read all extended attributes, and show their values (embeded pictures, lyrics and synchronised lyrics). But here I'm running into problems, raised by some mismatch between structures described in WMFSDK and those returned by IWMHeaderInfo3.GetAttributeByIndexEx Let's take attribute "WM/Lyrics_Synchronised". WMFSDK states that it's data array must contain such structure:
typedef struct _WMSynchronisedLyrics{ BYTE bTimeStampFormat; '(1 or 2 - depending on TimeStamp format used) BYTE bContentType; '(0-8 - ContentType dependant) LPWSTR pwszContentDescriptor; '(Pointer to a wide-character null-terminated string containing data from the encoding application; app-dependant(?)) DWORD dwLyricsLen; '(length of the lyrics data in bytes) BYTE* pbLyrics; '(Pointer to a BYTE array containing the lyrics)
} WM_SYNCHRONISED_LYRICS;
, while dataArray, returned by GetAttributeByIndexEx, contains this (by zero based position in array):
0(1-st byte) - bTimeStampFormat (2 in my case, meaning absolute time of the lyric in milliseconds) 1(2-nd byte) - bContentType (1 in my case, meaning song lyrics) 2(3-6 bytes) - dword, with unknown meaning, let's name it dwA 6(7-10 bytes) - dword, corresponding to dwLyricsLen 10(11-14 bytes) - dword, with unknown meaning, let's name it dwB 14 - wide-character null-terminated string, containing description of Synched lyrics attribute, and after it goes pbLyrics-BYTE array containing the lyrics data (timestamps & their strings) itself.
The most interesting thing is that dwords dwA and dwB are getting new, absolutely random at first sight values on every GetAttributeByIndexEx call on the same mp3 file! I say 'at first sight' because it's not absolutely true: difference dwB-dwA always remains the same for a file (but may differ for various files)! It would be only interesting, if I only wanted to read synched lyrics - I can simply ignore those dwA & dwB; and I do so, reading WM/Lyrics_Synchronised. But when you decide to update this attribute, you'll see, that you can't ignore them, leaving zeroed or even filling with some values according this strange 'constant difference' rule! That's why I ask for your help. Of course, it would be ideal to see some VB code sample for updating (IWMHeaderInfo3.ModifyAttribute), but I thank in advance for any ideas in vb.net or c#:)
Here is the same mismatch for WM/Picture for example: description in WMFSDK:
structure, returned by GetAttributeByIndexEx: 0: - dword with unknown meaning 4: - bPictureType (Picture type (author, album cover, group etc.) ) 5: - dword with unknown meaning 9: - dwDataLen 'Picture size 13: -dword with unknown meaning 17: -pwszMIMEType (wide-character null-terminated string, usualy "image/jpeg" in my case) then goes pwszDescription, then goes pbData
So, please, all guru, - help me to find a vb.net solution for WM attributes update!
tia, Vincas Stepankevicius
P.S. One idea came into my head while writing these notes - most likely those 'mystic' dwords means nothing else but addresses of pointers to data? Nonetheless I don't know what can I do with such 'knowledge', - so you remain my last hope :)
<rcsTAKE...@smithvoiceTAKEOUT.com> wrote: >I'll give a $10 prize (ten dollars US) via Paypal to anyone who can answer >this adequately using VB.Net or C# (without using UnSafe).
>Code to this group please, Vincas being the judge of the result.
Of cource, you may be not so savy as I expected, but please be at least more well-meant :) If my terrible English (I'm Lithanian) is the only reason you can't answer this question - it's not so bad ;)
In the simplest terms I just asked for a sample of VB.NET code for updating extended binary attributes of media files... If you are still hazy about purport of my request - please, be so kind - don't waste your time :)
"Vincas Stepankevicius" <vstepankevic...@vilnius.vmi.lt> wrote: > One idea came into my head while writing these notes - most likely > those 'mystic' dwords means nothing else but addresses of pointers to > data? Nonetheless I don't know what can I do with such 'knowledge', - > so you remain my last hope :)
They are pointers and the difference is a constant because the data is always laid out in th same way. You can access the data they point to using methods in System.Runtime.InteropServices.Marshal like PtrToStringUni(), PtrToStructure(), ReadByte(), ReadInt16()... I think there may be a more elegant solution, but this should do.
> They are pointers and the difference is a constant because the data is > always laid out in th same way. You can access the data they point to > using methods in System.Runtime.InteropServices.Marshal like > PtrToStringUni(), PtrToStructure(), ReadByte(), ReadInt16()... I > think there may be a more elegant solution, but this should do.
Of course, you can use the inverse methods to write new data and get pointers to your own structs and strings.
It is also useful to know that the Lyrics fields are derived from the MP3 definitions for such things which are documented in in various places.
I haven't looked at them in detail so the following may be wrong.
It *may* be that in practive the pointers are effectively offsets into the datablock so if you tread them as longs and see what is at point it may be pertinent.
Thanx, Alessandro, I'll try it, while one question still remains: which structure must be used in IWMHeaderInfo3.ModifyAttribute - structure described in SDK or one returned by GetAttributeByIndexEx? They are different even considering your explanation.
I'm glad to see that my English is not such bad as to be absolutely unclear :)
> > One idea came into my head while writing these notes - most likely > > those 'mystic' dwords means nothing else but addresses of pointers to > > data? Nonetheless I don't know what can I do with such 'knowledge', - > > so you remain my last hope :)
> They are pointers and the difference is a constant because the data is > always laid out in th same way. You can access the data they point to using > methods in System.Runtime.InteropServices.Marshal like PtrToStringUni(), > PtrToStructure(), ReadByte(), ReadInt16()... I think there may be a more > elegant solution, but this should do.
"Iain" <Iain...@idcl.co.uk> wrote: > It *may* be that in practive the pointers are effectively offsets > into the datablock so if you tread them as longs and see what is at > point it may be pertinent.
I don't think so. The data block where the pointers are embedded is marshalled as an unstructured byte array so they are not interpreted and hence must retain their valued in unmanaged code, where they are actual pointers and not offsets.
"Vincas Stepankevicius" <vstepankevic...@vilnius.vmi.lt> wrote: > I'll try it, while one question still remains: which structure must > be used in IWMHeaderInfo3.ModifyAttribute - structure described in > SDK or one returned by GetAttributeByIndexEx? They are different even > considering your explanation.
Both methods do not use any specific structure so I'll assume you mean the structure relative to a specific attribute type, for example WM_SYNCHRONISED_LYRICS, in which case the structure is exactly the same for both methods, no difference. Or maybe you meant that the buffer you get filled by GetAttributeByIndexEx() doesn't look like a WM_SYNCHRONISED_LYRICS structure? Since I do not have any file that contains such an attribute, I can not investigate it (do you have a sample you can share?).
>> It *may* be that in practive the pointers are effectively offsets >> into the datablock so if you tread them as longs and see what is at >> point it may be pertinent.
> I don't think so. The data block where the pointers are embedded is > marshalled as an unstructured byte array so they are not interpreted and > hence must retain their valued in unmanaged code, where they are actual > pointers and not offsets.
Yeah, but are you saying that the the WMFSDK knows enough to fix up these pointers as it loads the header metadata block into memory?
It would seem a remarkably clever thing to get it to do.
Mind you if you actually have tried this then I'll shut up, since I have not. I found out just enough to reckon it wasn't worth my while! A little knowledge and all that!
> Both methods do not use any specific structure so I'll assume you mean the > structure relative to a specific attribute type, for example > WM_SYNCHRONISED_LYRICS, in which case the structure is exactly the same for > both methods, no difference. Or maybe you meant that the buffer you get > filled by GetAttributeByIndexEx() doesn't look like a
WM_SYNCHRONISED_LYRICS
> structure?
I agree, that *internal* structure is the same for both methods, but as you can see from declaration of WMFSDKWrapper.dll method GetAttributeByIndexEx inVB: -- Public Overridable Function GetAttributeByIndexEx(ByVal wStreamNum As System.UInt16, ByVal wIndex As System.UInt16, ByVal pwszName As String, ByRef pwNameLen As System.UInt16, ByRef pType As WMFSDKWrapper.WMT_ATTR_DATATYPE, ByRef pwLangIndex As System.UInt16, ByVal pValue() As Byte, ByRef pdwDataLength As System.UInt32) As System.UInt32 -- , this method returns attribute data as as byte array pValue, and I must decide myself what structure has data in it, depending on attr name & type...
>Since I do not have any file that contains such an attribute, I > can not investigate it (do you have a sample you can share?).
Of cource I have, but I don't know if this group is a proper place for messages with audio files attached :) If You have Windows Media player (ver 9 or 10), you can simply create such attributes on any mp3 or wma file you have. If not - let me know, and I'll try to make a mp3 or wma file as little as possible, with required attributes set. If I got it right, I can use your spelled e-mail for sending such file. Correct me if I'm wrong.
Not at all - I wasn't being nasty ! I'm a web developer so I'm more or less clueless about what you were asking ;-)
The English was fine, it was the technical content that baffled me. It's probably a very interesting question, I just have no idea what the answer will be !
Hopefully, Alessandro has a few clues he can offer.
Kind regards - Neil
On Tue, 5 Oct 2004 13:03:44 +0300, "Vincas Stepankevicius"
>Of cource, you may be not so savy as I expected, but please be at least more >well-meant :) >If my terrible English (I'm Lithanian) is the only reason you can't answer >this question - it's not so bad ;)
>In the simplest terms I just asked for a sample of VB.NET code for updating >extended binary attributes of media files... >If you are still hazy about purport of my request - please, be so kind - >don't waste your time :)
>thanks, >Vincas
>"Neil Smith [MVP Digital Media]" <n...@nospam.com> wrote in message >news:o2n4m056oedn8v3bsausukcbtdngg5pe75@4ax.com... >> I'll consider giving $10 to anybody who understands and can explain to >> me a word he's on about Hehe. ;-)))
>> Cheers - Neil
>> On Mon, 04 Oct 2004 22:29:54 GMT, "smith" >> <rcsTAKE...@smithvoiceTAKEOUT.com> wrote:
>> >I'll give a $10 prize (ten dollars US) via Paypal to anyone who can >answer >> >this adequately using VB.Net or C# (without using UnSafe).
>> >Code to this group please, Vincas being the judge of the result.
> It is also useful to know that the Lyrics fields are derived from the MP3 > definitions for such things which are documented in in various places.
> I haven't looked at them in detail so the following may be wrong.
> It *may* be that in practive the pointers are effectively offsets into the > datablock so if you tread them as longs and see what is at point it may be > pertinent.
> Iain
Alessandro is positively right: they are *not* offsets in datablock for one simple reason: their values are just too big for that :)
There is absolutely no difference, which attr you'll examine. Having clearness with pic, you'll be done with slyr too and vice versa.
After visiting your site site thought that it would be nice to show you a screenshot of my app I've told you about. Unfortunately, I haven't any public website to publish it for you, so if you agree, I'll send it by e-mail. What app's doing: Attribute view/update (view - all attrs/update- all but those we are talking about); displays all embeded pics, lyrics, synched lyrics; scrolls synched lyrics in a karaoke-style while playing; plus has a very simple & handy lyrics synchronising interface (in contrast to awkward WMP10 one) So, the only thing I'm missing - binary attrs update (with simple ones, as I've told, I had no problems). That's why I'm here :)
Vincas
"smith" <rcsTAKE...@smithvoiceTAKEOUT.com> wrote in message
I'm glad about my English and Alessandro - I consider you being indebted him $10 ;-) Vincas "Neil Smith [MVP Digital Media]" <n...@nospam.com> wrote in message news:cmd5m0hm53phv4m3510fk57s4rk05n5qjf@4ax.com...
> Not at all - I wasn't being nasty ! I'm a web developer so I'm more or > less clueless about what you were asking ;-)
> The English was fine, it was the technical content that baffled me. > It's probably a very interesting question, I just have no idea what > the answer will be !
> Hopefully, Alessandro has a few clues he can offer.
> Kind regards - Neil
> On Tue, 5 Oct 2004 13:03:44 +0300, "Vincas Stepankevicius" > <vstepankevic...@vilnius.vmi.lt> wrote:
> >Hi, Neil,
> >Of cource, you may be not so savy as I expected, but please be at least more > >well-meant :) > >If my terrible English (I'm Lithanian) is the only reason you can't answer > >this question - it's not so bad ;)
> >In the simplest terms I just asked for a sample of VB.NET code for updating > >extended binary attributes of media files... > >If you are still hazy about purport of my request - please, be so kind - > >don't waste your time :)
> >thanks, > >Vincas
> >"Neil Smith [MVP Digital Media]" <n...@nospam.com> wrote in message > >news:o2n4m056oedn8v3bsausukcbtdngg5pe75@4ax.com... > >> I'll consider giving $10 to anybody who understands and can explain to > >> me a word he's on about Hehe. ;-)))
Thanx, Alessandro, I must rush now to study & test your sample (regrettably, I need some time to convert it Into VB) But now I see some "light in the end of tunnel" :) I'll be back tomorow,
You'll have to let me know when you get a winner here, I don't have the resources to make a redundant thread on my sitelett and keeping it to this group will help far more people.
Vincas Stepankevicius wrote: > Thanx, Alessandro, I must rush now to study & test your > sample (regrettably, I need some time to convert it Into > VB) > But now I see some "light in the end of tunnel" :) > I'll be back tomorow,
Unfortunately, I don't enough VB.NET to provide samples in that language in a short time. Here you are a better sample which also supports WM/Picture:
-------------------------------------- using System; using System.IO; using System.Runtime.InteropServices; using WMFSDKWrapper;
public class slyr { public static void Main(string[] args) { try { IWMMetadataEditor pEditor = null;
On Tue, 5 Oct 2004 22:30:50 +0300, "Vincas Stepankevicius"
<vstepankevic...@vilnius.vmi.lt> wrote: >I'm glad about my English and Alessandro - I consider you being indebted him >$10 ;-)
Damn !
>Vincas
Oh, yeah I see what you;re doing from the screenshots now. I had some similar problems to do an 'Export to Excel' (which was only partly tested) but I did it in javascript. It mostly falls down on the XML processing, and really needs to go client side :
The problem is, AFAIK the Excel ("Office Web Components") you can put into a web page, don't read *native* OfficeXML format files :-P
Anyway - cheers & good luck with this.
======================================================== CaptionKit http://www.captionkit.com : Produce subtitled internet media, transcripts and searchable video. Supports Real Player, Quicktime and Windows Media Player.
VideoChat with friends online, get Freshly Toasted every day at http://www.fresh-toast.net : NetMeeting solutions for a connected world.
FOR i AS Integer = 0 TO Convert.ToInt32(wCount)-1 DIM dtType AS WMT_ATTR_DATATYPE DIM wLang AS UInt16 DIM szName AS String = Nothing DIM pbData() AS Byte = Nothing DIM wName AS UInt16 DIM dwData AS UInt32
pHeader.GetAttributeByIndexEx(wStream,Convert.ToUInt16(i),sz Name,wName,dtType,wLang,pbData,dwData) szName = NEW String("0",Convert.ToInt32(wName)-1) REDIM pbData(Convert.ToInt32(dwData))
_end = _end + _ptr WHILE _ptr < _end DIM line AS String DIM time As Integer
line = Marshal.PtrToStringUni(NEW IntPtr(_ptr)) _ptr = _ptr + 2 * (1 + line.Length) time = Marshal.ReadInt32(NEW IntPtr(_ptr)) _ptr = _ptr + 4 Console.WriteLine(">>> [{0:d}:{1:d2}:{2:d2}.{3:d3}] L""{4}""",time\1000\60\60,(time\1000\60) mod 60,(time\1000) mod 60,time mod 1000,line) END WHILE ELSE IF szName = "WM/Picture" THEN Console.WriteLine(">>> pwszMIMEType .. = {0}",Marshal.PtrToStringUni(NEW IntPtr(pReader.ReadInt32()))) Console.WriteLine(">>> bPictureType .. = {0}",pReader.ReadByte()) Console.WriteLine(">>> pwszDescription = L""{0}""",Marshal.PtrToStringUni(NEW IntPtr(pReader.ReadInt32()))) _end = pReader.ReadInt32() Console.WriteLine(">>> dwDataLen ..... = {0}",_end) _ptr = pReader.ReadInt32() Console.WriteLine(">>> pbData ........ = ${0:X8}",_ptr)
DIM pFile AS FileStream = NEW FileStream("picture" & i & ".jpg",FileMode.OpenOrCreate,FileAccess.Write) DIM bas AS IntPtr = NEW IntPtr(_ptr) FOR off AS Integer = 0 TO _end-1 pFile.WriteByte(Marshal.ReadByte(bas,off)) NEXT off END IF NEXT i CATCH ex AS COMException Console.WriteLine(ex.ToString()) END TRY END SUB END MODULE
--------------------------------
--
// Alessandro Angeli // MVP :: Digital Media // a dot angeli at psynet dot net
Yep! Great job, Alessandro! Now it's too late(1:30) to check your code but it seems for me that you have allredy $20 from two Smith's - 10 from Robert + 10 from Neil ;) Forgive me, that I cant propose you nothing else but my respect and gratitude :) I see now how my problems can be solved but I'd appreciate if you find time to make an experiment with updating attrs ;)
> FOR i AS Integer = 0 TO Convert.ToInt32(wCount)-1 > DIM dtType AS WMT_ATTR_DATATYPE > DIM wLang AS UInt16 > DIM szName AS String = Nothing > DIM pbData() AS Byte = Nothing > DIM wName AS UInt16 > DIM dwData AS UInt32
> DIM pFile AS FileStream = NEW FileStream("picture" & i > & ".jpg",FileMode.OpenOrCreate,FileAccess.Write) > DIM bas AS IntPtr = NEW IntPtr(_ptr) > FOR off AS Integer = 0 TO _end-1 > pFile.WriteByte(Marshal.ReadByte(bas,off)) > NEXT off > END IF > NEXT i > CATCH ex AS COMException > Console.WriteLine(ex.ToString()) > END TRY > END SUB > END MODULE
> --------------------------------
> --
> // Alessandro Angeli > // MVP :: Digital Media > // a dot angeli at psynet dot net