Commit a105211b authored by Ramesh Nallur's avatar Ramesh Nallur

Updating vanilla mp4v2 files

parent 52d44e09
Background
==========
The mp4clip utility file uses the mp4v2 library to generate a modified mp4 file for fragmented and non-fragmented files and also generates an index file. The way to use the index file and what Rygel needs to do is mentioned below.
Things that are still pending that needs to be done,
-- mp4clip should be integrated with server-ingest.
-- Rygel needs to be modified to digest the index file information for time seek.
Building
========
Follow the instructions for building mp4v2. Please see the link
https://code.google.com/p/mp4v2/wiki/BuildSource#Configure
Running
=======
Once you build and install there should be a binary called mp4clip.
Run mp4clip in the following manner
mp4clip <MP4File>
NOTE: This will overwrite the mp4 file and add elst boxes and free boxes. A index file is also generated. The name of the index file <MP4File>.index
The index will contain the following information,
- One type of record starts with "M". This indicates the non-fragmented portion of the mp4 file. For e.g. the record will look like
M 8.406,0,4647,32,128842,15360,1,10195,32,370688,44100,
M -- The non-fragmented portion of the mp4 file
8.406 -- The duration of the non-fragmented portion in seconds. Any seek at or below this time should use this record.
0 -- Track index
4647 -- Offset into the file that needs to be modified with the requested time seek value. This units needs to be in timescale units.
32 -- Size of the duration field. 32 bit vs 64 bit
128842 -- Duration of the track
15360 -- TimeScale of the track. Duration/TimeScale is the duration of the track in seconds.
1 -- Track Index
10195 -- Offset into the file that needs to be modified with the requested time seek value. This units needs to be in timescale units.
32 -- Size of the duration field. 32 bit vs 64 bit
370688 -- Duration of the track
44100 - TimeScale of the track. Duration/TimeScale is the duration of the track in seconds.
Rygel at the time of time seek is expected to do the following,
- Read the offset location for each track and modify the location with the time seek value in the TimeScale units.
- Second type of record starts with "F". This indicates the fragmented portion of the mp4 file
F 11.9429,18099878,0,18110298,1,18111214,
F -- The fragmented portion of the file. If there are multiple fragments there will be multiple records of fragments
11.9429 -- The current time line of the track in seconds. This is the timeline the fragment ends. So if you want to see before you need to read from the offset below.
18099878 -- The location where the ftyp starts. This is the location from where the data needs to be read if one wants to seek before 11.9429 seconds
0 -- Index of the trun that needs to be changed. Its just an FYI
18110298 -- The offset into the file that contains the moof offset information. This location needs to be modified by reading the value at this location and substracting with the offset where the ftyp starts (the third column).
1 -- Index of the trun that needs to be changed.
18111214 -- The offset into the file that contains the moof offset information. This location needs to be modified by reading the value at this location and substracting with the offset where the ftyp starts (the third column).
/* This is an example of iTMF Generic API.
* WARNING: this program will change/destroy certain tags in an mp4 file.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <mp4v2/mp4v2.h>
int main( int argc, char** argv )
{
if( argc != 2 ) {
printf( "usage: %s file.mp4\n", argv[0] );
return 1;
}
/* open file for modification */
MP4FileHandle file = MP4Modify( argv[1], MP4_DETAILS_ERROR, 0 );
if( file == MP4_INVALID_FILE_HANDLE ) {
printf( "MP4Modify failed\n" );
return 1;
}
/* show existing iTMF items */
MP4ItmfItemList* list = MP4ItmfGetItems( file );
printf( "list=%p\n", list );
if( list ) {
printf( "list size=%u\n", list->size );
uint32_t i;
for( i = 0; i < list->size; i++ ) {
MP4ItmfItem* item = &list->elements[i];
printf( "item[%u] type=%s\n", i, item->code );
if( item->mean )
printf( " mean=%s\n", item->mean );
if( item->name )
printf( " name=%s\n", item->name );
int j;
for( j = 0; j < item->dataList.size; j++ ) {
MP4ItmfData* data = &item->dataList.elements[j];
printf( " data[%u] typeCode=%u valueSize=%u\n", j, data->typeCode, data->valueSize );
}
}
/* caller responsiblity to free */
MP4ItmfItemListFree( list );
}
/* add bogus item to file */
{
/* allocate item with 1 data element */
MP4ItmfItem* bogus = MP4ItmfItemAlloc( "bogu", 1 );
const char* const hello = "hello one";
MP4ItmfData* data = &bogus->dataList.elements[0];
data->typeCode = MP4_ITMF_BT_UTF8;
data->valueSize = strlen( hello );
data->value = (uint8_t*)malloc( data->valueSize );
memcpy( data->value, hello, data->valueSize );
/* add to mp4 file */
MP4ItmfAddItem( file, bogus );
/* caller responsibility to free */
MP4ItmfItemFree( bogus );
}
/* add bogus item with meaning and name to file */
{
/* allocate item with 1 data element */
MP4ItmfItem* bogus = MP4ItmfItemAlloc( "----", 1 );
bogus->mean = strdup( "com.garden.Tomato" );
bogus->name = strdup( "weight" );
const char* const hello = "hello two";
MP4ItmfData* data = &bogus->dataList.elements[0];
data->typeCode = MP4_ITMF_BT_UTF8;
data->valueSize = strlen( hello );
data->value = (uint8_t*)malloc( data->valueSize );
memcpy( data->value, hello, data->valueSize );
/* add to mp4 file */
MP4ItmfAddItem( file, bogus );
/* caller responsibility to free */
MP4ItmfItemFree( bogus );
}
/* free memory associated with structure and close */
MP4Close( file );
return 0;
}
/* This is an example of iTMF Tags convenience API.
* WARNING: this program will change/destroy many tags in an mp4 file.
*/
#include <mp4v2/mp4v2.h>
int main( int argc, char** argv )
{
if( argc != 2 ) {
printf( "usage: %s file.mp4\n", argv[0] );
return 1;
}
/* open file for modification */
MP4FileHandle file = MP4Modify( argv[1], MP4_DETAILS_ERROR, 0 );
if( file == MP4_INVALID_FILE_HANDLE ) {
printf( "MP4Modify failed\n" );
return 1;
}
/* allocate */
const MP4Tags* tags = MP4TagsAlloc();
/* fetch data from MP4 file and populate structure */
MP4TagsFetch( tags, file );
/***************************************************************************
* print various tag values
*/
if( tags->name )
printf( "name: %s\n", tags->name );
if( tags->artist )
printf( "artist: %s\n", tags->artist );
if( tags->albumArtist )
printf( "albumArtist: %s\n", tags->albumArtist );
if( tags->album )
printf( "album: %s\n", tags->album );
if( tags->grouping )
printf( "grouping: %s\n", tags->grouping );
if( tags->composer )
printf( "composer: %s\n", tags->composer );
if( tags->comments )
printf( "comments: %s\n", tags->comments );
if( tags->genre )
printf( "genre: %s\n", tags->genre );
if( tags->genreType )
printf( "genreType: %u\n", *tags->genreType );
if( tags->releaseDate )
printf( "releaseDate: %s\n", tags->releaseDate );
if( tags->track )
printf( "track: index=%u total=%u\n", tags->track->index, tags->track->total );
if( tags->disk )
printf( "disk: index=%u total=%u\n", tags->disk->index, tags->disk->total );
if( tags->tempo )
printf( "tempo: %u\n", *tags->tempo );
if( tags->compilation )
printf( "compilation: %u\n", *tags->compilation );
if( tags->tvShow )
printf( "tvShow: %s\n", tags->tvShow );
if( tags->tvNetwork )
printf( "tvNetwork: %s\n", tags->tvNetwork );
if( tags->tvEpisodeID )
printf( "tvEpisodeID: %s\n", tags->tvEpisodeID );
if( tags->tvSeason )
printf( "tvSeason: %u\n", *tags->tvSeason );
if( tags->tvEpisode )
printf( "tvEpisode: %u\n", *tags->tvEpisode );
if( tags->description )
printf( "description: %s\n", tags->description );
if( tags->longDescription )
printf( "longDescription: %s\n", tags->longDescription );
if( tags->lyrics )
printf( "lyrics: %s\n", tags->lyrics );
if( tags->sortName )
printf( "sortName: %s\n", tags->sortName );
if( tags->sortArtist )
printf( "sortArtist: %s\n", tags->sortArtist );
if( tags->sortAlbumArtist )
printf( "sortAlbumArtist: %s\n", tags->sortAlbumArtist );
if( tags->sortAlbum )
printf( "sortAlbum: %s\n", tags->sortAlbum );
if( tags->sortComposer )
printf( "sortComposer: %s\n", tags->sortComposer );
if( tags->sortTVShow )
printf( "sortTVShow: %s\n", tags->sortTVShow );
if( tags->artworkCount ) {
const MP4TagArtwork* art = tags->artwork; /* artwork != NULL when artworkCount > 0 */
uint32_t i;
for( i = 0; i < tags->artworkCount; i++, art++ )
printf( "art[%d]: type=%d size=%u data=%p\n", i, art->type, art->size, art->data );
}
if( tags->copyright )
printf( "copyright: %s\n", tags->copyright );
if( tags->encodingTool )
printf( "encodingTool: %s\n", tags->encodingTool );
if( tags->encodedBy )
printf( "encodedBy: %s\n", tags->encodedBy );
if( tags->purchaseDate )
printf( "purchaseDate: %s\n", tags->purchaseDate );
if( tags->podcast )
printf( "podcast: %u\n", *tags->podcast );
if( tags->keywords )
printf( "keywords: %s\n", tags->keywords );
if( tags->category )
printf( "category: %s\n", tags->category );
if( tags->hdVideo )
printf( "hdVideo: %u\n", *tags->hdVideo );
if( tags->mediaType )
printf( "mediaType: %u\n", *tags->mediaType );
if( tags->contentRating )
printf( "contentRating: %u\n", *tags->contentRating );
if( tags->gapless )
printf( "gapless: %u\n", *tags->gapless );
if( tags->contentID )
printf( "contentID: %u\n", *tags->contentID );
if( tags->artistID )
printf( "artistID: %u\n", *tags->artistID );
if( tags->playlistID )
printf( "playlistID: %llu\n", *tags->playlistID );
if( tags->genreID )
printf( "genreID: %u\n", *tags->genreID );
if( tags->xid )
printf( "xid: %s\n", tags->xid );
if( tags->iTunesAccount )
printf( "iTunesAccount: %s\n", tags->iTunesAccount );
if( tags->iTunesAccountType )
printf( "iTunesAccountType: %u\n", *tags->iTunesAccountType );
if( tags->iTunesCountry )
printf( "iTunesCountry: %u\n", *tags->iTunesCountry );
/**************************************************************************
* modify various tags values
*/
uint8_t n8;
uint16_t n16;
uint32_t n32;
uint64_t n64;
MP4TagTrack dtrack;
dtrack.index = 1;
dtrack.total = 1;
MP4TagDisk ddisk;
ddisk.index = 1;
ddisk.total = 1;
MP4TagsSetName ( tags, "my name" );
MP4TagsSetArtist ( tags, "my artist" );
MP4TagsSetAlbumArtist ( tags, "my albumArtist" );
MP4TagsSetAlbum ( tags, "my album" );
MP4TagsSetGrouping ( tags, "my grouping" );
MP4TagsSetComposer ( tags, "my composer" );
MP4TagsSetComments ( tags, "my comments" );
MP4TagsSetGenre ( tags, "my genre" );
n16 = 5; /* disco */
MP4TagsSetGenreType ( tags, &n16 );
MP4TagsSetReleaseDate ( tags, "my releaseDate" );
MP4TagsSetTrack ( tags, &dtrack );
MP4TagsSetDisk ( tags, &ddisk );
n16 = 60; /* bpm */
MP4TagsSetTempo ( tags, &n16 );
n8 = 0; /* false */
MP4TagsSetCompilation ( tags, &n8 );
MP4TagsSetTVShow ( tags, "my tvShow" );
MP4TagsSetTVNetwork ( tags, "my tvNetwork" );
MP4TagsSetTVEpisodeID ( tags, "my tvEpisodeID" );
n32 = 0;
MP4TagsSetTVSeason ( tags, &n32 );
n32 = 0;
MP4TagsSetTVEpisode ( tags, &n32 );
MP4TagsSetDescription ( tags, "my description" );
MP4TagsSetLongDescription ( tags, "my longDescription" );
MP4TagsSetLyrics ( tags, "my lyrics" );
MP4TagsSetSortName ( tags, "my sortName" );
MP4TagsSetSortArtist ( tags, "my sortArtist" );
MP4TagsSetSortAlbumArtist ( tags, "my sortAlbumArtist" );
MP4TagsSetSortAlbum ( tags, "my sortAlbum" );
MP4TagsSetSortComposer ( tags, "my sortComposer" );
MP4TagsSetSortTVShow ( tags, "my sortTVShow" );
MP4TagsSetCopyright ( tags, "my copyright" );
MP4TagsSetEncodingTool ( tags, "my encodingTool" );
MP4TagsSetEncodedBy ( tags, "my encodedBy" );
MP4TagsSetPurchaseDate ( tags, "my purchaseDate" );
n8 = 0; /* false */
MP4TagsSetPodcast ( tags, &n8 );
MP4TagsSetKeywords ( tags, "my keywords" );
MP4TagsSetCategory ( tags, "my category" );
n8 = 0; /* false */
MP4TagsSetHDVideo ( tags, &n8 ); // false
n8 = 9; /* movie */
MP4TagsSetMediaType ( tags, &n8 ); // movie
n8 = 0; /* none */
MP4TagsSetContentRating ( tags, &n8 ); // none
n8 = 0; /* false */
MP4TagsSetGapless ( tags, &n8 ); // false
MP4TagsSetITunesAccount ( tags, "my iTunesAccount" );
n8 = 0; /* iTunes */
MP4TagsSetITunesAccountType ( tags, &n8 );
n32 = 143441; /* USA */
MP4TagsSetITunesCountry ( tags, &n32 );
n32 = 0;
MP4TagsSetContentID ( tags, &n32 );
n32 = 0;
MP4TagsSetArtistID ( tags, &n32 );
n64 = 0;
MP4TagsSetPlaylistID ( tags, &n64 );
n32 = 0;
MP4TagsSetGenreID ( tags, &n32 );
n32 = 0;
MP4TagsSetComposerID ( tags, &n32 );
MP4TagsSetXID ( tags, "my prefix:my scheme:my identifier" );
/* push data to mp4 file */
MP4TagsStore( tags, file );
/* free memory associated with structure and close */
MP4TagsFree( tags );
MP4Close( file );
return 0;
}
/* This example makes use of the MP4FileProvider API to use custom file
* input/output routines.
*/
#include <mp4v2/mp4v2.h>
#include <stdio.h>
/*****************************************************************************/
static void* my_open( const char* name, MP4FileMode mode )
{
const char* om;
switch( mode ) {
case FILEMODE_READ: om = "rb"; break;
case FILEMODE_MODIFY: om = "r+b"; break;
case FILEMODE_CREATE: om = "w+b"; break;
case FILEMODE_UNDEFINED:
default:
om = "rb";
break;
}
return fopen( name, om );
}
static int my_seek( void* handle, int64_t pos )
{
return fseeko( (FILE*)handle, pos, SEEK_SET ) != 0;
}
static int my_read( void* handle, void* buffer, int64_t size, int64_t* nin, int64_t maxChunkSize )
{
if( fread( buffer, size, 1, (FILE*)handle ) != 1)
return 1;
*nin = size;
return 0;
}
static int my_write( void* handle, const void* buffer, int64_t size, int64_t* nout, int64_t maxChunkSize )
{
if( fwrite( buffer, size, 1, (FILE*)handle ) != 1)
return 1;
*nout = size;
return 0;
}
static int my_close( void* handle )
{
return fclose( (FILE*)handle ) != 0;
}
/*****************************************************************************/
int main( int argc, char** argv )
{
if( argc != 2 ) {
printf( "usage: %s file.mp4\n", argv[0] );
return 1;
}
/* populate data structure with custom functions.
* safe to put on stack as it will be immediately copied internally.
*/
MP4FileProvider provider;
provider.open = my_open;
provider.seek = my_seek;
provider.read = my_read;
provider.write = my_write;
provider.close = my_close;
/* open file for read */
MP4FileHandle file = MP4ReadProvider( argv[1], 0, &provider );
if( file == MP4_INVALID_FILE_HANDLE ) {
printf( "MP4Read failed\n" );
return 1;
}
/* dump file contents */
if( !MP4Dump( file, stdout, 0 ))
printf( "MP4Dump failed\n" );
/* cleanup and close */
MP4Close( file );
return 0;
}
This diff is collapsed.
......@@ -7,6 +7,8 @@
#ifdef __MINGW32__
# undef __MSVCRT_VERSION__
# define __MSVCRT_VERSION__ 0x800
// JAN: see http://code.google.com/p/mp4v2/issues/detail?id=132
# define _USE_32BIT_TIME_T
#endif
// set minimum win32 API requirement to Windows 2000 or higher
......@@ -58,6 +60,8 @@ namespace mp4v2 { namespace platform {
///////////////////////////////////////////////////////////////////////////////
// If using Visual Studio 2010 or newer these aren't needed since stdint.h is available.
#if !(defined(_MSC_VER) && _MSC_VER >= 1600)
// some macros for constant expressions
#define INT8_C(x) x
#define INT16_C(x) x
......@@ -68,6 +72,7 @@ namespace mp4v2 { namespace platform {
#define UINT16_C(x) x
#define UINT32_C(x) x ## UL
#define UINT64_C(x) x ## ULL
#endif
///////////////////////////////////////////////////////////////////////////////
......
// Note that we have a separate platform_win32_impl.h to deal with the fact that windows.h defines a macro
// called FindAtom, which mp4v2 also defines. In older versions of visual studio, this actually causes
// some pretty seriously issues with naming collisions and the defined macros (think infamous min/max macro
// of windows.h vs stdc++'s min/max template functions)
#include <windows.h>
///////////////////////////////////////////////////////////////////////////////
namespace mp4v2 { namespace platform { namespace win32 {
class Utf8ToFilename
{
public:
Utf8ToFilename( const string &utf8string );
~Utf8ToFilename( );
bool IsUTF16Valid( ) const;
operator LPCWSTR( ) const { return _wideCharString; }
operator LPWSTR( ) const { return _wideCharString; }
private:
Utf8ToFilename ( const Utf8ToFilename &src );
Utf8ToFilename &operator= ( const Utf8ToFilename &src );
wchar_t *ConvertToUTF16 ( const string &utf8 );
static int ConvertToUTF16Buf ( const char *utf8,
wchar_t *utf16_buf,
size_t num_bytes );
static int GetPrefixLen ( const string &utf8string );
static int IsAbsolute ( const string &utf8string );
static int IsPathSeparator ( char c );
static int IsUncPath ( const string &utf8string );
static const UINT8 *Utf8DecodeChar (
const UINT8 *utf8_char,
size_t num_bytes,
wchar_t *utf16,
int *invalid
);
static size_t Utf8LenFromUcs4 ( UINT32 ucs4 );
static UINT8 Utf8NumOctets ( UINT8 utf8_first_byte );
/**
* The UTF-8 encoding of the filename actually used
*/
string _utf8;
/**
* The UTF-16 encoding of the filename actually used
*/
wchar_t* _wideCharString;
public:
/**
* Accessor for @p _utf8
*/
const string& utf8;
};
}}} // namespace mp4v2::platform::win32
Maintainer Add Source Files
***************************
Almost any new .cpp or .h files will have to be added to GNUmakefile.am .
It is trivial to add hand-written source files. Just add them to the list
for the particular library or program which already exists. For example,
to add a source file to libmp4v2:
libmp4v2_la_SOURCES = ...
In the rare situation that you desire to add a header file to be installed
during 'make install' and used by the public API (such as mp4v2.h) then it
should be taken out of libmp4v2_la_SOURCES and added to:
mp4v2inc_HEADERS = ...
Once done, autotool files such as configure will need to be regenerated.
This is usually done automatically for you by the generated makefile,
but the following command is always an excellent way to make sure
everything is regenerated properly:
autoreconf -fiv
Don't be alarmed at the number of files created by autoreconf. These
files will be part of any dist-bundle created but do not add them to the
repository -- they are dist-only files.
Maintainer Autotools Instructions
*********************************
This project uses autotools and does not add/checkin files generated by
autoreconf to the repository. These files are to be included only in
dist-bundles.
There is a clear distinction between building from the repository or
building from a dist-bundle. This document is for project maintainers
building from the repository.
The maintainer is responsible for generating files associated with
autotools. These files in turn are distributed with dist-bundles, but
never should be added/checked-in to the repository.
Whenever any information for which autotools may depend on is changed,
then you must issue the following command which will force generation
of all files and install support scripts required at configure-time:
autoreconf -fiv
Where is Makefile.am? And what is this GNUmakefile stuff?
We already have a sophisticated project which requires enough GNU-tools
at build-time that it makes no sense to hope-and-pray that BSD flavors
of make will work. As such, the files are renamed to be those which are
only recognized by GNU make. This helps avoid confusion when people run
the wrong make flavor -- it's better to see Makefile not found, rather
than 100 obscure make incompatibility-errors. So we have GNUmakefile.am
which is just like the old Makefile.am only better.
Post configure, we end up with GNUmakefile in your build directory.
Care has been taken to avoid subdir makefiles. And while they may
eventually become part of this project, it's nice to avoid them when
when possible.
Maintainer Release Instructions
*******************************
Official releases are made by creating a dist-bundle as supported by
the autotools framework. This is not a normal target for users building
the library to use, and as such it may introduce extra tool requirements.
For example, 'makeinfo' is required to generate some documentation before