locked
MP3 stream metadata from network source RRS feed

  • Question

  • Is there any possible way to get access to the MP3 metadata sent by most MP3 streaming servers (such as Icecast) through Media Foundation, or is the only possible option to write your own network source from scratch?
    Wednesday, January 20, 2010 8:48 PM

Answers

  • IMFSchemeHandler is present on Vista, but individual scheme handler implementations may be Windows 7+.  The Urlmon scheme handler is Windows 7+.  There is no MF shoutcast support in Vista.

    As far as the bleeps, I think this may be a side effect of the server not responding with ICY 200.  If the server does not get recognized as a shoutcast server, shoutcast metadata will not be parsed.  http://91.121.125.91:8006 is a shoutcast URL with updating metadata; you can give that a try if you want to see it working.  Icecast servers may just not be supported.
    • Marked as answer by The March Hare Saturday, February 20, 2010 7:54 AM
    Saturday, February 6, 2010 2:12 AM

All replies

  • MF has an IMFByteStream that supports shoutcast/icecast streaming.  You can create a source that connects to an icecast URL using the source resolver.  If you query the source for the MF_METADATA_PROVIDER_SERVICE, it will delegate metadata calls to the bytestream where applicable.  See Media Source Metadata for documentation on how to get and use the metadata provider service.  The mapping from shoutcast names to IMFMetadata property names is as follows:

        { L"icy-name", g_wszWMTitle },
        { L"x-audiocast-name", g_wszWMTitle  },
        { L"icy-genre", g_wszWMGenre },
        { L"x-audiocast-genre", g_wszWMGenre },
        { L"icy-url", g_wszWMPromotionURL },
        { L"x-audiocast-url", g_wszWMPromotionURL },
        { L"x-audiocast-description", g_wszWMDescription },
        { L"x-audiocast-artist", g_wszWMAuthor },
        { L"x-audiocast-album", g_wszWMAlbumTitle },
        { L"x-audiocast-br", g_wszWMBitrate },
        { L"icy-br", g_wszWMBitrate },
    Friday, January 22, 2010 8:35 PM
  • Thanks. I've tried though, and the problem I'm having is that all I can get are these:

    Bitrate=...
    Title=...
    WM/Genre=...
    WM/PromotionURL=...

    This is just static data that describes the entire presentation, but I can't get the dynamic metadata (track title) that is continously sent as part of the stream. I believe this is something you actually have to request as part of the HTTP header when you connect, otherwise it is not sent (presumably for compatibility with clients that can't parse it.)

    Is this something that is supported by Media Foundation? I don't believe Windows Media Player supports it, so maybe not?


    Here's the test code just for reference:


    IMFGetService *pMetaService = NULL;
    MFCALL(m_pSource->QueryInterface(&pMetaService));
    if(SUCCEEDED(hr))
    {
       IMFMetadataProvider *pMetaProvider = NULL;
       MFCALL(pMetaService->GetService(
          MF_METADATA_PROVIDER_SERVICE, IID_PPV_ARGS(&pMetaProvider)));
          
       if(SUCCEEDED(hr))
       {
    
          IMFPresentationDescriptor *pPD = NULL;
          MFCALL(m_pSource->CreatePresentationDescriptor(&pPD));
    
          if(SUCCEEDED(hr))
          {
    
             IMFMetadata *pMetaData = NULL;
             MFCALL(pMetaProvider->GetMFMetadata(pPD, 0, 0, &pMetaData));
             
             if(SUCCEEDED(hr))
             {
                wprintf(L"Meta:\n");
                MFDumpMeta(pMetaData);
    
                SR(&pMetaData);
             }
    
             SR(&pPD);
          }
          
          SR(&pMetaProvider);
       }
    
       SR(&pMetaService);
    }
    
    Friday, January 22, 2010 9:38 PM
  • The title of the stream is updated as the server sends metadata updates, and is the only property that is updated during streaming.  This assumes that the server sent the icy-metaint header which indicates the interval of metadata updates; we do not support any other methods of updating metadata if they exist.  The bytestream will send an MESourceMetadataChanged event when it encounters a title change.  This event will be propagated up through the source, and when you receive the event you should get the new title value in response to a query for g_wszWMTitle.
    Saturday, January 23, 2010 2:00 AM
  • The server does send icy-metaint. The only problem is that it only sends it if the client included "Icy-MetaData: 1" in its request header. In other words, you have to opt-in to receive it.

    Here is a capture of what another client sends and gets in return:

    - Http: Request, GET /stream 
        Command: GET
      - URI: /stream
         Location: /stream 
        ProtocolVersion: HTTP/1.0
        Host:  mms-live.online.no
        UserAgent:  WinampMPEG/5.57, Ultravox/2.1
        Ultravox-transport-type:  TCP
        Accept:  */*
        Icy-MetaData: 1
        Connection:  close
        HeaderEnd: CRLF
    
    - Http: Response, HTTP/1.0, Status: Ok, URL: /stream 
        ProtocolVersion: HTTP/1.0
        StatusCode: 200, Ok
        Reason: OK
      + ContentType:  audio/mpeg
        icy-br: 192
        ice-audio-info:  ice-samplerate=44100;ice-bitrate=192;ice-channels=2
        icy-br: 192
        icy-genre: ...
        icy-name: ...
        icy-pub: 0
        icy-url: ...
        Server:  Icecast 2.3.2
        Cache-Control:  no-cache
        icy-metaint: 16000
        HeaderEnd: CRLF
    

    And here is the same attempt with Media Foundation:

    - Http: Request, GET /stream
        Command: GET
      - URI: /stream
         Location: /stream
        ProtocolVersion: HTTP/1.1
        Cache-Control:  no-cache
        Connection:  Keep-Alive
        Pragma:  getIfoFileURI.dlna.org
        Accept:  */*
        UserAgent:  NSPlayer/12.00.7600.16385 WMFSDK/12.00.7600.16385
        GetContentFeatures.DLNA.ORG:  1
        Host:  mms-live.online.no
        HeaderEnd: CRLF
    
    - Http: Response, HTTP/1.0, Status: Ok, URL: /stream 
        ProtocolVersion: HTTP/1.0
        StatusCode: 200, Ok
        Reason: OK
      + ContentType:  audio/mpeg
        icy-br: 192
        ice-audio-info:  ice-samplerate=44100;ice-bitrate=192;ice-channels=2
        icy-br: 192
        icy-genre: ...
        icy-name: ...
        icy-pub: 0
        icy-url: ... 
        Server:  Icecast 2.3.2
        Cache-Control:  no-cache
        HeaderEnd: CRLF
    

    As you can see, MF does not appear to request metadata. You seem to be saying that it is capable of parsing it, so is there any way to make it actually request it? As far as I can tell this is a requirement of the protocol and no servers will send icy-metaint and the actual meta data packets without first being asked with the icy-metadata header.
    Saturday, January 23, 2010 4:40 PM
  • The expected response from a shoutcast server is ICY 200 OK.  When the MF download bytestream encounters this shoutcast response, it disconnects, reconnects, and sends a new request with an icy-metadata: 1 header.  It looks like this flavor of *cast server does not respond in this way, and MF performs a normal progressive download of the stream.

    If you directly create the urlmon scheme plugin (CLSID_UrlmonSchemePlugin, interface IMFSchemeHandler) and resolve the URL using the scheme handler directly, it will bypass the download bytestream and send the icy-metadata header on the first request.  You would then need to call the source resolver to resolve a source.  This only works well if you know in advance that a URL you have points to a *cast server, unfortunately.
    Thursday, January 28, 2010 1:39 AM
  • The standard HTTP response I posted above seems to be what all Icecast servers I have tried send. They all appear to be version 2.3.2. Perhaps it runs in some sort of compatibility mode by default, I don't know?

    Anyway, I have just tried your suggestion, and I can't seem to get very far. I'm new to this, so please bear with me here.

    IMFSchemeHandler *pSchemeHandler = NULL;
    hr = CoCreateInstance(CLSID_UrlmonSchemePlugin,
       NULL, CLSCTX_INPROC_SERVER,
       IID_PPV_ARGS(&pSchemeHandler));
    
    MFCALL(pSchemeHandler->BeginCreateObject(pUrl,
       MF_RESOLUTION_BYTESTREAM, NULL, NULL,
       pShoutCallback, NULL));
       
    [...]
    
    MF_OBJECT_TYPE type;
    IUnknown *pUnknown = NULL;
    MFCALL(m_pSchemeHandler->EndCreateObject(
       pAsyncResult, &type, &pUnknown));
    
    IMFByteStream *pByteStream = NULL;
    hr = pUnknown->QueryInterface(&pByteStream);
    SR(&pUnknown);
    
    IMFAttributes *pAttribs = NULL;
    hr = pByteStream->QueryInterface(&pAttribs);
    MFDumpAttributes(pAttribs);
    
    IMFSourceResolver *pResolver = NULL;
    MFCALL(MFCreateSourceResolver(&pResolver));
    MFCALL(pResolver->CreateObjectFromByteStream(
       pByteStream, NULL, MF_RESOLUTION_MEDIASOURCE,
       NULL, &type, &pUnknown));

    This fails with MF_E_UNSUPPORTED_BYTESTREAM_TYPE. The bytestream has no attributes set either. I tried manually setting the content type to "audio/mpeg", but then CreateObjectFromByteStream never returns. Any ideas?
    Monday, February 1, 2010 9:43 PM
  • Setting the MF_BYTESTREAM_CONTENT_TYPE to audio/mpeg is the right thing to do.  The fact that it does not return an error immediately indicates that it found a bytestream handler for the MIME type, which is a good sign.  The only known reason why CreateObjectFromURL/CreateObjectFromByteStream never returns is if the platform is not started (ie, MFStartup was not called) or if another callback is blocking the standard workqueue.  Are you trying to call CreateObjectFromByteStream from within the callback function for BeginCreateObject? 
    Tuesday, February 2, 2010 3:37 AM
  • I was calling it from the callback, yes. Silly mistake.

    I moved it out of the callback and it creates the source fine now. The only problem is that it seems to be trying to play it as a regular MP3 stream, meaning the meta data shows up as audible bleeps rather than being parsed.

    Also, are the scheme interfaces only supported on Windows 7+?
    Tuesday, February 2, 2010 8:27 PM
  • IMFSchemeHandler is present on Vista, but individual scheme handler implementations may be Windows 7+.  The Urlmon scheme handler is Windows 7+.  There is no MF shoutcast support in Vista.

    As far as the bleeps, I think this may be a side effect of the server not responding with ICY 200.  If the server does not get recognized as a shoutcast server, shoutcast metadata will not be parsed.  http://91.121.125.91:8006 is a shoutcast URL with updating metadata; you can give that a try if you want to see it working.  Icecast servers may just not be supported.
    • Marked as answer by The March Hare Saturday, February 20, 2010 7:54 AM
    Saturday, February 6, 2010 2:12 AM
  • Yeah, that works. It's just Icecast.

    How does it know to parse the meta data though? Is this all internal to the bytestream, or is there a flag that can be set on anything?

    Thanks for all your help anyway.
    Saturday, February 6, 2010 11:32 AM
  • It looks like it is all internal to the bytestream.  If the bytestream does not get the ICY 200 response, it does not search for metadata.  This is not great behavior and something we will look into fixing for the future.
    Wednesday, February 10, 2010 1:25 AM
  • Hi, I am trying to do the same; reading the ICY data from a shoutcast station.
    Do you have any sample code to show how it can be done?
    Regards 

    Friday, August 27, 2010 8:34 AM