Share via


MusicBundleProduction.cpp

/*****************************************************************************
*
* File: MusicBundleProduction.cpp
*
* Description:
* This sample is a simple application that might be used as a starting-point
* for an application that uses the Packaging API. This sample demonstrates
* the production and consumption of a music bundle package, which is a custom
* package file-format.
*
* The music bundle format is an example of a custom package file format and
* was designed specifically for this sample. See the MusicBundle.h header file
* for the music bundle specification.
*
* For the sake of simplicity, production data used for the sample is hard
* coded. In a fully-functional application, production data would be retrieved
* from the user of the application.
*
* ------------------------------------
*
*  This file is part of the Microsoft Windows SDK Code Samples.
* 
*  Copyright (C) Microsoft Corporation.  All rights reserved.
* 
* This source code is intended only as a supplement to Microsoft
* Development Tools and/or on-line documentation.  See these other
* materials for detailed information regarding Microsoft code samples.
* 
* THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
* PARTICULAR PURPOSE.
*****************************************************************************/

#include <stdio.h>
#include <windows.h>
#include <shlobj.h>

#include <msopc.h>  // For Packaging APIs

#include <strsafe.h>

#include "util.h"
#include "MusicBundle.h"
#include "urlmon.h"

//---------------------------------------
// For the sake of simplicity, the production data that appears below is hard
// coded. In a fully-functional application, this data would be retrieved from
// the user of the application.

// Track names.
static LPCWSTR g_trackNames[] = {
    L"\\Tracks\\CrystalFree.wma",
    L"\\Tracks\\Sire.wma",
    L"\\Tracks\\SmallPines.wma",
    L"\\Tracks\\Valparaiso.wma"
};

// Lyric file names. One-to-one mapping with track names.
static LPCWSTR g_lyricsNames[] = {
    L"\\Lyrics\\CrystalFree.txt",
    L"\\Lyrics\\Sire.txt",
    L"\\Lyrics\\SmallPines.txt",
    L"\\Lyrics\\Valparaiso.txt"
};

// Album art.
static const WCHAR g_albumArt[] = L"\\AlbumArt\\jacqui.jpg";

// Track list.
static const WCHAR g_trackList[] = L"\\TrackList.wpl";

// Link to external website for the album.
static const WCHAR g_albumWebsite[] = L"http://www.example.com/Media/Albums/Jacqui%20Kramer/Leap%20Forward";

//-------------------------------------
// Production helper methods.

///////////////////////////////////////////////////////////////////////////////
// Description:
// Creates an empty part, adds the part to the package's part set and writes
// data to the part as part content.
//
// Note: This method does not add Relationships parts to the music bundle.
///////////////////////////////////////////////////////////////////////////////
HRESULT
AddPartToBundle(
    IOpcFactory  *opcFactory,
    LPCWSTR  contentFileName,     // File that contains the content to be stored in the part to add.
    IOpcPartSet  *packagePartSet, // Represents the set of parts (excluding Relationships parts)
                                  // in a package.
    LPCWSTR  contentType,         // The content type of the content to be stored in the part.
    OPC_COMPRESSION_OPTIONS  compressionOptions, // Level of compression to use on the part.
    LPCWSTR  inputDirectory,      // Directory location of the file specified in contentFileName.
    IOpcPartUri  **createdPartUri,// Represents the part name. The caller must release the interface.
    IOpcPart  **createdPart = NULL// Optional. Represents the part to add to the package. The caller must
                                  // release the interface.
    )
{
    HRESULT hr = S_OK;
    IOpcPart * part = NULL;

    if (createdPartUri == NULL)
    {
        // The createdPartUri parameter is required.
        hr = E_INVALIDARG;
    }

    if (SUCCEEDED(hr))
    {
        // Create the part name from the file name.
        hr = opcFactory->CreatePartUri(contentFileName, createdPartUri);
    }

    if (SUCCEEDED(hr))
    {
        // Create the part as an empty part, and add it to the set of parts
        // in the package.
        hr = packagePartSet->CreatePart(
                *createdPartUri,
                contentType,
                compressionOptions,
                &amp;part
                );
    }

    if (SUCCEEDED(hr))
    {
        // Add content to the empty part.
        hr = WriteFileContentToPart(
                opcFactory,
                inputDirectory,
                contentFileName,
                part
                );
    }
    
    if (SUCCEEDED(hr) &amp;&amp; createdPart != NULL)
    {
        // The createdPart out parameter was specified.
        // Return the interface pointer to the part created and added to the
        // set of parts in the package.
        *createdPart = part;
        part = NULL;
    }

    // Release resources
    if (part)
    {
        part->Release();
        part = NULL;
    }
    
    return hr;
}

///////////////////////////////////////////////////////////////////////////////
// Description:
// Adds a Track part and a Lyrics part to the set.
// 1. Track part:
//     + Adds a Track part to the music bundle
//     + Creates a relationship where the source is the Track List part and the
//       target is the Track part by creating the relationship from the Track
//       List part's relationship set.
// 2. Lyrics part:
//     + Adds a Lyrics part to the music bundle.
//     + Creates a relationship where the source is the Track part that was 
//       added to the music bundle and the target is the Track part by creating
//       the relationship from the Track part's relationship set.
///////////////////////////////////////////////////////////////////////////////
HRESULT
AddTrackAndLyricsToBundle(
    IOpcFactory  *opcFactory,
    IOpcPartSet  *packagePartSet, // Represents the set of parts (excluding Relationships parts) in
                                  // a package.
    IOpcRelationshipSet  *trackListRelationshipSet, // Represents the Relationships part containing
                                                    // relationships that have the Track List part
                                                    // as their target.
    LPCWSTR inputDirectory,       // Directory location of the files specified in trackName and 
                                  // lyricsName.
    LPCWSTR trackName,            // Name of file that contains the track data.
    LPCWSTR lyricsName            // Name of tile that contains lyrics data.
    
    )
{
    HRESULT hr = S_OK;
    IOpcPartUri * trackPartUri = NULL;
    IOpcPartUri * lyricsPartUri = NULL;
    IOpcPart * trackPart = NULL;
    IOpcRelationshipSet * trackRelationshipSet = NULL;

    // Add a Track part that contains track data to the package.
    hr = AddPartToBundle(
            opcFactory,
            trackName,
            packagePartSet,
            g_trackContentType,
            OPC_COMPRESSION_NONE,
            inputDirectory,
            &amp;trackPartUri,
            &amp;trackPart
            );

    if (SUCCEEDED(hr))
    {
        // Add a relationship that has the Track part as the target to the
        // relationship set that represents the Relationships part containing
        // relationships that have the Track List part as their source.
        hr = trackListRelationshipSet->CreateRelationship(
                NULL,                         // Use auto-generated relationship ID.
                g_trackRelationshipType,
                trackPartUri,                 // Relationship target's URI.
                OPC_URI_TARGET_MODE_INTERNAL, // Relationship's target is internal.
                NULL                          // No pointer needed since returned relationship not required.
                );
    }

    
    if (SUCCEEDED(hr))
    {
        // Add a Lyrics part that contains lyrics for added track.
        hr = AddPartToBundle(
                opcFactory,
                lyricsName,
                packagePartSet,
                g_lyricsContentType,
                OPC_COMPRESSION_NORMAL,
                inputDirectory,
                &amp;lyricsPartUri
                );
    }

    if (SUCCEEDED(hr))
    {
        // Get relationship set for Track part. The relationship set
        // represents the Relationship part that stores relationships that have
        // the Track part as their source. 
        hr = trackPart->GetRelationshipSet(&amp;trackRelationshipSet);
    }

    if (SUCCEEDED(hr))
    {
        // Add a relationship to the Track part's Relationships part,
        // represented as a relationship object in the relationship object set.
        hr = trackRelationshipSet->CreateRelationship(
                NULL,                         // Use auto-generated relationship ID.
                g_lyricsRelationshipType,
                lyricsPartUri,                // Relationship target's URI.
                OPC_URI_TARGET_MODE_INTERNAL, // Relationship's target is internal.
                NULL                          // No pointer needed since returned relationship not required.
                );
    }
    
    // Release resources
    if (trackPartUri)
    {
        trackPartUri->Release();
        trackPartUri = NULL;
    }

    if (lyricsPartUri)
    {
        lyricsPartUri->Release();
        lyricsPartUri = NULL;
    }

    if (trackPart)
    {
        trackPart->Release();
        trackPart = NULL;
    }

    if (trackRelationshipSet)
    {
        trackRelationshipSet->Release();
        trackRelationshipSet = NULL;
    }

    return hr;
}

///////////////////////////////////////////////////////////////////////////////
// Description:
// Adds the Album Art part to the package and creates a package relationship to
// that has the Album Art part as its target.
///////////////////////////////////////////////////////////////////////////////
HRESULT
AddAlbumArtToBundle(
    IOpcFactory  *opcFactory,
    IOpcPartSet  *packagePartSet, // Represents the set of parts (excluding Relationships parts)
                                  // in a package.
    IOpcRelationshipSet  *packageRelationshipSet, // Represents the Relationships part that stores
                                                  // package relationships.
    LPCWSTR  inputDirectory       // Directory location of the files specified in trackName and
                                  // lyricsName.
    )
{
    HRESULT hr = S_OK;
    IOpcPartUri * albumArtPartUri = NULL;
    
    // Add Album Art part.
    hr = AddPartToBundle(
            opcFactory,
            g_albumArt,
            packagePartSet,
            g_albumArtContentType,
            OPC_COMPRESSION_NONE,
            inputDirectory,
            &amp;albumArtPartUri
            );
    
    if (SUCCEEDED(hr))
    {
        // Add a package relationship that has the Album Art part as its target.
        hr = packageRelationshipSet->CreateRelationship(
                NULL,                         // Use auto-generated relationship ID.
                g_albumArtRelationshipType,
                albumArtPartUri,              // Relationship target's URI.
                OPC_URI_TARGET_MODE_INTERNAL, // Relationship's target is internal.
                NULL                          // No pointer needed since returned relationship not required.
                );
    }

    // Release resources
    if (albumArtPartUri)
    {
        albumArtPartUri->Release();
        albumArtPartUri = NULL;
    }
    
    return hr;
}

///////////////////////////////////////////////////////////////////////////////
// Description:
// Adds the Track List part to the package, and creates and adds a package
// relationship to the package relationship set that has the Track List part as
// its target, and then adds the track and lyrics to the music bundle.
///////////////////////////////////////////////////////////////////////////////
HRESULT
AddTrackListToBundle(
    IOpcFactory  *opcFactory,
    IOpcPartSet  *packagePartSet, // Represents the set of parts (excluding Relationships parts)
                                  // in a package.
    IOpcRelationshipSet  *packageRelationshipSet, // The relationship set that stores package
                                                  // relationships.
    LPCWSTR  inputDirectory       // Directory location of the files specified in trackName and
                                  // lyricsName.
    )
{
    HRESULT hr = S_OK;
    IOpcPartUri * trackListPartUri = NULL;
    IOpcPart * trackListPart = NULL;
    IOpcRelationshipSet * trackListRelationshipSet = NULL;

    // Add Track List part.
    hr = AddPartToBundle(
            opcFactory,
            g_trackList,
            packagePartSet,
            g_trackListContentType,
            OPC_COMPRESSION_NORMAL,
            inputDirectory,
            &amp;trackListPartUri,
            &amp;trackListPart
            );
    
    if (SUCCEEDED(hr))
    {
        // Add a package relationship that has the Track List part as its
        // target.
        hr = packageRelationshipSet->CreateRelationship(
                NULL,                         // Use auto-generated relationship ID.
                g_trackListRelationshipType,
                trackListPartUri,             // Relationship target's URI.
                OPC_URI_TARGET_MODE_INTERNAL, // Relationship's target is internal.
                NULL                          // No pointer needed since returned relationship not required.
                );
    }

    if (SUCCEEDED(hr))
    {
        // Get the relationship set that represents the Relationships part that
        // stores the relationships that have the Track List part as their
        // source.
        hr = trackListPart->GetRelationshipSet(&amp;trackListRelationshipSet);
    }
    
    if(SUCCEEDED(hr))
    {
        // Add all track and lyric files to the music bundle as Track parts
        // and Lyric parts, respectively.
        for (int i = 0; i < countof(g_trackNames) &amp;&amp; SUCCEEDED(hr); i++)
        {
            hr = AddTrackAndLyricsToBundle(
                    opcFactory, 
                    packagePartSet,
                    trackListRelationshipSet, 
                    inputDirectory, 
                    g_trackNames[i], 
                    g_lyricsNames[i]
                    );
        }
    }

    // Release resources
    if (trackListPartUri)
    {
        trackListPartUri->Release();
        trackListPartUri = NULL;
    }

    if (trackListPart)
    {
        trackListPart->Release();
        trackListPart = NULL;
    }

    if (trackListRelationshipSet)
    {
        trackListRelationshipSet->Release();
        trackListRelationshipSet = NULL;
    }

    return hr;
}

///////////////////////////////////////////////////////////////////////////////
// Description: 
// Creates a package relationship that targets the album website and adds the
// relationship to the package relationship set. 
///////////////////////////////////////////////////////////////////////////////
HRESULT
AddAlbumWebsiteToBundle(
    IOpcRelationshipSet  *packageRelationshipSet // Relationship set that has all package relationships.
    )
{
    HRESULT hr = S_OK;
    IUri * uri = NULL;

    // Create the URI for the Album Website.
    hr = CreateUri(
            g_albumWebsite,
            Uri_CREATE_CANONICALIZE,
            0,
            &amp;uri
            );

    if (SUCCEEDED(hr))
    {
        // Add Album Website as a package relationship. Note that the target
        // mode of the relationship is "External" because the website is
        // a resource that exists outside of the package.
        hr = packageRelationshipSet->CreateRelationship(
                NULL,                         // Use auto-generated relationship ID.
                g_albumWebsiteRelationshipType,
                uri,                          // Relationship target's URI.
                OPC_URI_TARGET_MODE_EXTERNAL, // Relationship's target is external.
                NULL                          // No pointer needed since returned relationship not required.
                );
    }
    
    // Release resources
    if (uri)
    {
        uri->Release();
        uri = NULL;
    }

    return hr;
}

//---------------------------------
// Function to create the new music bundle.
// Exposed through MusicBundle.h.
//
///////////////////////////////////////////////////////////////////////////////
// Description:
// Creates a package that is a music bundle, in compliance with both the OPC
// specification and the Music Bundle specification, which can be found in 
// MusicBundle.h. Given the directory that contains all the files needed, this
// method creates all parts and relationships for the new music bundle and
// saves resultant package.
//
// Note: Relationships parts are not created explicitly. Relationship sets are
// serialized as Relationships parts when the package is saved.
///////////////////////////////////////////////////////////////////////////////
HRESULT
ProduceMusicBundle(
    LPCWSTR inputDirectory,   // Parent directory that contains files to add to the music bundle.
    LPCWSTR outputPackageName // Name of the music bundle package to create.
    )
{
    HRESULT hr = S_OK;

    IOpcFactory * opcFactory = NULL;
    IOpcPackage * opcPackage = NULL;
    IOpcPartSet * packagePartSet = NULL;
    IOpcRelationshipSet * packageRelationshipSet = NULL;
    IStream * fileStream = NULL;

    // Create a new factory.
    hr = CoCreateInstance(
            __uuidof(OpcFactory),
            NULL,
            CLSCTX_INPROC_SERVER,
            __uuidof(IOpcFactory),
            (LPVOID*)&amp;opcFactory
            );

    if (SUCCEEDED(hr))
    {
        // Create the new, empty music bundle.
        hr = opcFactory->CreatePackage(&amp;opcPackage);
    }
    
    if (SUCCEEDED(hr))
    {
        // Get the set of parts in the package. Parts (that are not 
        // Relationships parts) to be included in the music bundle to be
        // created will be created in this set.
        hr = opcPackage->GetPartSet(&amp;packagePartSet);
    }

    if (SUCCEEDED(hr))
    {
        // Get the set of package relationships. All package relationships
        // specific to the music bundle to be created will be created in this
        // set.
        hr = opcPackage->GetRelationshipSet(&amp;packageRelationshipSet);
    }
    
    // Populate the music bundle.

    if (SUCCEEDED(hr))
    {
        // Add the Album Art part to the package.
        hr = AddAlbumArtToBundle(
                opcFactory,
                packagePartSet,
                packageRelationshipSet,
                inputDirectory
                );
    }
    if (SUCCEEDED(hr))
    {
        // Add the Track List part, and Track and Lyrics parts to package.
        hr = AddTrackListToBundle(
                opcFactory,
                packagePartSet,
                packageRelationshipSet,
                inputDirectory
                );
    }
    if (SUCCEEDED(hr))
    {
        // Add a package relationship that targets the album website to the
        // package.
        hr = AddAlbumWebsiteToBundle(packageRelationshipSet);
    }

    // Save the music bundle.

    if (SUCCEEDED(hr))
    {
        // Create a writable stream over the name of the package to be created.
        hr = opcFactory->CreateStreamOnFile(
                outputPackageName,
                OPC_STREAM_IO_WRITE,
                NULL,
                FILE_ATTRIBUTE_NORMAL,
                &amp;fileStream
                );
    }
    if (SUCCEEDED(hr))
    {
        // Serialize package content to the writable stream.
        hr = opcFactory->WritePackageToStream(
                opcPackage,
                OPC_WRITE_DEFAULT,
                fileStream
                );
    }

    // Release resources
    if (opcFactory)
    {
        opcFactory->Release();
        opcFactory = NULL;
    }

    if (opcPackage)
    {
        opcPackage->Release();
        opcPackage = NULL;
    }

    if (packagePartSet)
    {
        packagePartSet->Release();
        packagePartSet = NULL;
    }

    if (packageRelationshipSet)
    {
        packageRelationshipSet->Release();
        packageRelationshipSet = NULL;
    }

    if (fileStream)
    {
        fileStream->Release();
        fileStream = NULL;
    }

    return hr;
}

Music Bundle Sample