blob: cdfc441c0d589a688311596ab215c1cb11adf99c [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
******************************************************************************
* @file M4MP4W_Writer.c
* @brief Implementation of the core MP4 writer
******************************************************************************
*/
#include "NXPSW_CompilerSwitches.h"
#ifndef _M4MP4W_USE_CST_MEMORY_WRITER
#include "M4OSA_Error.h"
#include "M4OSA_Debug.h"
#include "M4MP4W_Writer.h"
#include "M4MP4W_Utils.h"
/* Check optimisation flags : BEGIN */
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
#ifdef _M4MP4W_MOOV_FIRST
#error "_M4MP4W_OPTIMIZE_FOR_PHONE should not be used with _M4MP4W_MOOV_FIRST"
#endif
#endif
#ifdef _M4MP4W_UNBUFFERED_VIDEO
#ifndef _M4MP4W_OPTIMIZE_FOR_PHONE
#error "_M4MP4W_UNBUFFERED_VIDEO should be used with _M4MP4W_OPTIMIZE_FOR_PHONE"
#endif
#endif
/* Check optimisation flags : END */
#ifndef _M4MP4W_DONT_USE_TIME_H
#include <time.h>
#endif /*_M4MP4W_DONT_USE_TIME_H*/
/*MACROS*/
#define MAJOR_VERSION 3
#define MINOR_VERSION 3
#define REVISION 0
#define ERR_CHECK(exp, err) if (!(exp)) { return err; }
#define CLEANUPonERR(func) if ((err = func) != M4NO_ERROR) goto cleanup
#define max(a,b) (((a) > (b)) ? (a) : (b))
/***************/
/*Static blocks*/
/***************/
/*CommonBlocks*/
const M4OSA_UChar Default_ftyp [] =
{
0x00, 0x00, 0x00, 0x18, 'f', 't', 'y', 'p', '3', 'g', 'p', '7', 0x00, 0x00,
0x03, 0x00, '3', 'g', 'p', '7', 'i', 's', 'o', 'm'
};
const M4OSA_UChar CommonBlock2 [] =
{
'm', 'd', 'a', 't'
};
const M4OSA_UChar CommonBlock3 [] =
{
'm', 'o', 'o', 'v', 0x00, 0x00, 0x00, 0x6C, 'm', 'v', 'h', 'd', 0x00,
0x00, 0x00, 0x00
};
const M4OSA_UChar CommonBlock4 [] =
{
0x00, 0x00, 0x03, 0xE8
};
const M4OSA_UChar CommonBlock5 [] =
{
0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03
};
const M4OSA_UChar CommonBlock6 [] =
{
't', 'r', 'a', 'k', 0x00, 0x00, 0x00, 0x5C, 't', 'k', 'h', 'd', 0x00,
0x00, 0x00, 0x01
};
const M4OSA_UChar CommonBlock7 [] =
{
0x00, 0x00, 0x00, 0x00
};
const M4OSA_UChar CommonBlock7bis [] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00
};
const M4OSA_UChar CommonBlock8 [] =
{
'm', 'd', 'i', 'a', 0x00, 0x00, 0x00, 0x20, 'm', 'd', 'h', 'd', 0x00,
0x00, 0x00, 0x00
};
const M4OSA_UChar CommonBlock9 [] =
{
0x55, 0xC4, 0x00, 0x00
};
const M4OSA_UChar CommonBlock10 [] =
{
'm', 'i', 'n', 'f', 0x00, 0x00, 0x00, 0x24, 'd', 'i', 'n', 'f', 0x00,
0x00, 0x00, 0x1C, 'd', 'r', 'e', 'f', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x0C, 'u', 'r', 'l', ' ', 0x00, 0x00, 0x00,
0x01
};
const M4OSA_UChar CommonBlock11 [] =
{
's', 't', 'b', 'l'
};
const M4OSA_UChar CommonBlock12 [] =
{
's', 't', 't', 's', 0x00, 0x00, 0x00, 0x00
};
const M4OSA_UChar SampleDescriptionHeader [] =
{
's', 't', 's', 'd', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
};
const M4OSA_UChar SampleDescriptionEntryStart [] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
};
const M4OSA_UChar CommonBlock15 [] =
{
's', 't', 's', 'z', 0x00, 0x00, 0x00, 0x00
};
const M4OSA_UChar CommonBlock16 [] =
{
's', 't', 's', 'c', 0x00, 0x00, 0x00, 0x00
};
const M4OSA_UChar CommonBlock17 [] =
{
's', 't', 'c', 'o', 0x00, 0x00, 0x00, 0x00
};
const M4OSA_UChar BlockSignatureSkipHeader [] =
{
0x00, 0x00, 0x00, 0x5E, 's', 'k', 'i', 'p'
};
/* due to current limitations, size must be 16 */
const M4OSA_UChar BlockSignatureSkipDefaultEmbeddedString [] =
{
'N', 'X', 'P', 'S', 'W', ' ', 'C', 'A', 'M', 'C', 'O', 'R', 'D', 'E',
'R', ' '
};
/* follows the version (like " 3.0.2"), then " -- " */
/* due to current limitations, size must be 60 */
const M4OSA_UChar BlockSignatureSkipDefaultIntegrationTag [] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/*VideoBlocks*/
/* 320*240, now no longer hardcoded */
/* const M4OSA_UChar VideoBlock1[] =
{ 0x01, 0x40, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00 }; */
const M4OSA_UChar VideoBlock1_1 [] =
{
0x00, 0x00, 0x00, 0x21, 'h', 'd', 'l', 'r', 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 'v', 'i', 'd', 'e', 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const M4OSA_UChar SampleDescriptionEntryVideoBoilerplate1 [] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const M4OSA_UChar SampleDescriptionEntryVideoBoilerplate2 [] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF
};
const M4OSA_UChar VideoBlock4 [] =
{
's', 't', 's', 's', 0x00, 0x00, 0x00, 0x00
}; /*STSS*/
const M4OSA_UChar VideoBlock5 [] =
{
0x00, 0x00, 0x00, 0x14, 'v', 'm', 'h', 'd', 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const M4OSA_UChar VideoResolutions [] =
{
0x00, 0x48, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00
};
/*Mp4vBlocks*/
const M4OSA_UChar Mp4vBlock1 [] =
{
'm', 'p', '4', 'v'
};
const M4OSA_UChar Mp4vBlock3 [] =
{
0x20, 0x11
};
/*H263Blocks*/
const M4OSA_UChar H263Block1 [] =
{
's', '2', '6', '3'
};
const M4OSA_UChar H263Block2 [] =
{
0x00, 0x00, 0x00, 0x0F, 'd', '2', '6', '3'
};
const M4OSA_UChar H263Block2_bitr [] =
{
0x00, 0x00, 0x00, 0x1F, 'd', '2', '6', '3'
};
const M4OSA_UChar H263Block3 [] =
{
'P', 'H', 'L', 'P', 0x00, 0x0A, 0x00
};
const M4OSA_UChar H263Block4 [] =
{
0x00, 0x00, 0x00, 0x10, 'b', 'i', 't', 'r'
};
/*H264Blocks*/
const M4OSA_UChar H264Block1 [] =
{
'a', 'v', 'c', '1'
};
/* Store the avcC field, the version (=1),
the profile (=66), the compatibility (=0), */
/* the level (=10),111111 + NAL field Size (= 4 - 1),
111 + number of PPS (=1) */
const M4OSA_UChar H264Block2 [] =
{
// Remove the hardcoded DSI values of H264Block2
'a' , 'v' , 'c' , 'C'
};
/*AMRBlocks*/
const M4OSA_UChar AMRBlock1 [] =
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const M4OSA_UChar AMRBlock1_1 [] =
{
0x00, 0x00, 0x00, 0x21, 'h', 'd', 'l', 'r', 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 's', 'o', 'u', 'n', 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
const M4OSA_UChar AudioSampleDescEntryBoilerplate [] =
{
0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00
};
const M4OSA_UChar AMRDSIHeader [] =
{
0x00, 0x00, 0x00, 0x11, 'd', 'a', 'm', 'r'
};
const M4OSA_UChar AMRDefaultDSI [] =
{
'P', 'H', 'L', 'P', 0x00, 0x00, 0x80, 0x00, 0x01
};
const M4OSA_UChar AMRBlock4 [] =
{
0x00, 0x00, 0x00, 0x10, 's', 'm', 'h', 'd', 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00
};
/*AMR8Blocks*/
const M4OSA_UChar AMR8Block1 [] =
{
's', 'a', 'm', 'r'
};
/*AMR16Blocks*/
/*const M4OSA_UChar AMR16Block1[] = { 's', 'a', 'w', 'b'};*/
/*AACBlocks*/
const M4OSA_UChar AACBlock1 [] =
{
'm', 'p', '4', 'a'
};
const M4OSA_UChar AACBlock2 [] =
{
0x40, 0x15
};
/*MPEGConfigBlocks (AAC & MP4V)*/
const M4OSA_UChar MPEGConfigBlock0 [] =
{
'e', 's', 'd', 's', 0x00, 0x00, 0x00, 0x00, 0x03
};
const M4OSA_UChar MPEGConfigBlock1 [] =
{
0x00, 0x00, 0x00, 0x04
};
const M4OSA_UChar MPEGConfigBlock2 [] = { 0x05 };
const M4OSA_UChar MPEGConfigBlock3 [] =
{
0x06, 0x01, 0x02
};
/*EVRCBlocks*/
const M4OSA_UChar EVRCBlock3_1 [] =
{
0x00, 0x00, 0x00, 0x0E, 'd', 'e', 'v', 'c'
};
const M4OSA_UChar EVRCBlock3_2 [] =
{
'P', 'H', 'L', 'P', 0x00, 0x00
};
/*EVRC8Blocks*/
const M4OSA_UChar EVRC8Block1 [] =
{
's', 'e', 'v', 'c'
};
/***********/
/* Methods */
/***********/
/*******************************************************************************/
M4OSA_ERR M4MP4W_getVersion(M4OSA_UInt8 *major, M4OSA_UInt8 *minor,
M4OSA_UInt8 *revision )
/*******************************************************************************/
{
ERR_CHECK(M4OSA_NULL != major, M4ERR_PARAMETER);
ERR_CHECK(M4OSA_NULL != minor, M4ERR_PARAMETER);
ERR_CHECK(M4OSA_NULL != revision, M4ERR_PARAMETER);
*major = MAJOR_VERSION;
*minor = MINOR_VERSION;
*revision = REVISION;
return M4NO_ERROR;
}
static M4OSA_UInt32 M4MP4W_STTS_ALLOC_SIZE;
static M4OSA_UInt32 M4MP4W_STSZ_ALLOC_SIZE;
static M4OSA_UInt32 M4MP4W_STSS_ALLOC_SIZE;
static M4OSA_UInt32 M4MP4W_CHUNK_ALLOC_NB;
static M4OSA_UInt32 M4MP4W_STTS_AUDIO_ALLOC_SIZE;
static M4OSA_UInt32 M4MP4W_STSZ_AUDIO_ALLOC_SIZE;
static M4OSA_UInt32 M4MP4W_CHUNK_AUDIO_ALLOC_NB;
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
#ifdef _M4MP4W_UNBUFFERED_VIDEO
/* stsc[ ] table is splitted at 12 bits */
#define M4MP4W_VIDEO_MAX_AU_PER_CHUNK 4095 /* 0=notused */
#else
#define M4MP4W_VIDEO_MAX_AU_PER_CHUNK 10 /* 0=notused */
#endif
#endif
/*******************************************************************************/
M4OSA_ERR M4MP4W_initializeAllocationParameters(M4MP4W_Mp4FileData *Ptr )
/*******************************************************************************/
{
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
M4OSA_UInt32 maxMemory, vesMemory;
M4OSA_UInt32 nbVideoFrames, nbAudioFrames;
M4OSA_UInt32 averageVideoChunk;
/*-----------*/
/* NB_FRAMES */
/*-----------*/
/* magical formula : memory = vesMemory + 12 * framerate * duration */
#ifdef _M4MP4W_UNBUFFERED_VIDEO
vesMemory = 0x32000; /* 200 kB */
#else
vesMemory = 0x3E800; /* 250 kB */
#endif
#define VIDEO_POOL_MEMORY 1000000
maxMemory = VIDEO_POOL_MEMORY;
if (maxMemory < vesMemory) {
return M4ERR_ALLOC;
}
nbVideoFrames = ( maxMemory - vesMemory) / 12;
M4OSA_TRACE1_1("M4MP4W: %d images max", nbVideoFrames);
/* VIDEO */
#ifdef _M4MP4W_UNBUFFERED_VIDEO
/* assume an average of 25 fpc : reference = 15 fps * 2s * 0.8 */
averageVideoChunk = 2500;
#else
if (M4MP4W_VIDEO_MAX_AU_PER_CHUNK > 0)
{
averageVideoChunk = 100 * M4MP4W_VIDEO_MAX_AU_PER_CHUNK - 20
* (M4MP4W_VIDEO_MAX_AU_PER_CHUNK - 1); /* margin 20% */
}
else
{
/* assume an average of 50 fpc */
averageVideoChunk = 5000;
}
#endif
M4MP4W_STTS_ALLOC_SIZE = nbVideoFrames * sizeof(M4OSA_UInt32);
M4MP4W_STSZ_ALLOC_SIZE = nbVideoFrames * sizeof(M4OSA_UInt16);
M4MP4W_STSS_ALLOC_SIZE = nbVideoFrames * sizeof(
M4OSA_UInt32); /* very conservative (all images are intra) */
M4MP4W_CHUNK_ALLOC_NB = ( nbVideoFrames * 100) / averageVideoChunk + 1;
/* AUDIO */
nbAudioFrames = nbVideoFrames;
/* audio is 5 fps, which is the smallest framerate for video */
M4MP4W_STTS_AUDIO_ALLOC_SIZE = 100; /* compressed */
M4MP4W_STSZ_AUDIO_ALLOC_SIZE = 100; /* compressed */
#ifdef _M4MP4W_UNBUFFERED_VIDEO
M4MP4W_CHUNK_AUDIO_ALLOC_NB = nbAudioFrames / 10 + 1;
#else
M4MP4W_CHUNK_AUDIO_ALLOC_NB = nbAudioFrames / 38 + 1;
#endif
return M4NO_ERROR;
#else
/* VIDEO 5 min at 25 fps null-enc */
M4MP4W_STTS_ALLOC_SIZE = 20000;
M4MP4W_STSZ_ALLOC_SIZE = 18000;
M4MP4W_STSS_ALLOC_SIZE = 5000;
M4MP4W_CHUNK_ALLOC_NB = 500;
/* AUDIO 2 min aac+ null-enc */
M4MP4W_STTS_AUDIO_ALLOC_SIZE = 32000;
M4MP4W_STSZ_AUDIO_ALLOC_SIZE = 20000;
M4MP4W_CHUNK_AUDIO_ALLOC_NB = 1000;
return M4NO_ERROR;
#endif /*_M4MP4W_OPTIMIZE_FOR_PHONE*/
}
/*******************************************************************************/
M4OSA_ERR M4MP4W_openWrite(M4OSA_Context *contextPtr,
void *outputFileDescriptor,
M4OSA_FileWriterPointer *fileWriterFunction,
void *tempFileDescriptor,
M4OSA_FileReadPointer *fileReaderFunction )
/*******************************************************************************/
{
M4OSA_ERR err = M4NO_ERROR;
M4MP4W_Mp4FileData *mMp4FileDataPtr = M4OSA_NULL;
ERR_CHECK(M4OSA_NULL != contextPtr, M4ERR_PARAMETER);
ERR_CHECK(M4OSA_NULL != outputFileDescriptor, M4ERR_PARAMETER);
ERR_CHECK(M4OSA_NULL != fileWriterFunction, M4ERR_PARAMETER);
#ifdef _M4MP4W_RESERVED_MOOV_DISK_SPACE
/* Optional, feature won't be used if NULL */
M4OSA_TRACE2_1("tempFileDescriptor = %p", tempFileDescriptor);
if (M4OSA_NULL == tempFileDescriptor)
{
M4OSA_TRACE1_0(
"tempFileDescriptor is NULL, RESERVED_MOOV_DISK_SPACE feature not used");
}
#else /* _M4MP4W_RESERVED_MOOV_DISK_SPACE */
/* Not used : ERR_CHECK(M4OSA_NULL != tempFileDescriptor, M4ERR_PARAMETER); */
#endif /* _M4MP4W_RESERVED_MOOV_DISK_SPACE */
/* Not used : ERR_CHECK(M4OSA_NULL != fileReaderFunction, M4ERR_PARAMETER); */
/* The context reuse mode was suppressed*/
mMp4FileDataPtr =
(M4MP4W_Mp4FileData *)M4OSA_32bitAlignedMalloc(sizeof(M4MP4W_Mp4FileData),
M4MP4_WRITER, (M4OSA_Char *)"MP4 writer context");
ERR_CHECK(mMp4FileDataPtr != M4OSA_NULL, M4ERR_ALLOC);
mMp4FileDataPtr->url = outputFileDescriptor;
mMp4FileDataPtr->audioTrackPtr = M4OSA_NULL;
mMp4FileDataPtr->videoTrackPtr = M4OSA_NULL;
mMp4FileDataPtr->MaxChunkSize = M4MP4W_DefaultMaxChunkSize; /*default */
mMp4FileDataPtr->MaxAUSize = M4MP4W_DefaultMaxAuSize; /*default */
mMp4FileDataPtr->InterleaveDur =
M4MP4W_DefaultInterleaveDur; /*default = 0, i.e. not used*/
mMp4FileDataPtr->MaxFileSize = 0; /*default = 0, i.e. not used*/
mMp4FileDataPtr->camcoderVersion = 0; /*default is " 0.0.0"*/
mMp4FileDataPtr->embeddedString =
M4OSA_NULL; /*default is in BlockSignatureSkipDefaultEmbeddedString */
mMp4FileDataPtr->integrationTag = M4OSA_NULL; /*default is 0 */
mMp4FileDataPtr->MaxFileDuration = 0; /*default = 0, i.e. not used*/
mMp4FileDataPtr->fileWriterFunctions = fileWriterFunction;
mMp4FileDataPtr->hasAudio = M4OSA_FALSE;
mMp4FileDataPtr->hasVideo = M4OSA_FALSE;
mMp4FileDataPtr->state = M4MP4W_opened;
mMp4FileDataPtr->duration = 0; /*i*/
/*patch for integrationTag 174 -> 238 (+64)*/
mMp4FileDataPtr->filesize =
238; /*initialization with constant part in ftyp+mdat+moov+skip*/
mMp4FileDataPtr->estimateAudioSize = M4OSA_FALSE;
mMp4FileDataPtr->audioMsChunkDur =
0; /*set and used only when estimateAudioSize is true*/
mMp4FileDataPtr->audioMsStopTime =
0; /*set and used only when estimateAudioSize is true*/
mMp4FileDataPtr->fileWriterContext = M4OSA_NULL;
/* + CRLV6775 -H.264 trimming */
mMp4FileDataPtr->bMULPPSSPS = M4OSA_FALSE;
/* - CRLV6775 -H.264 trimming */
#ifndef _M4MP4W_MOOV_FIRST
mMp4FileDataPtr->absoluteCurrentPos =
32; /*init with ftyp + beginning of mdat size*/
#endif
#ifdef _M4MP4W_RESERVED_MOOV_DISK_SPACE
mMp4FileDataPtr->safetyFileUrl = tempFileDescriptor;
mMp4FileDataPtr->cleanSafetyFile =
M4OSA_FALSE; /* No need to clean it just yet. */
#endif /* _M4MP4W_RESERVED_MOOV_DISK_SPACE */
/* ftyp atom */
memset((void *) &mMp4FileDataPtr->ftyp,0,
sizeof(mMp4FileDataPtr->ftyp));
*contextPtr = mMp4FileDataPtr;
M4MP4W_initializeAllocationParameters(mMp4FileDataPtr);
return err;
}
/*******************************************************************************/
M4OSA_ERR M4MP4W_addStream(M4OSA_Context context,
M4SYS_StreamDescription *streamDescPtr )
/*******************************************************************************/
{
M4OSA_ERR err = M4NO_ERROR;
M4MP4W_Mp4FileData *mMp4FileDataPtr = (M4MP4W_Mp4FileData *)context;
ERR_CHECK(M4OSA_NULL != context, M4ERR_PARAMETER);
ERR_CHECK(( mMp4FileDataPtr->state == M4MP4W_opened)
|| (mMp4FileDataPtr->state == M4MP4W_ready), M4ERR_STATE);
mMp4FileDataPtr->state = M4MP4W_ready;
switch (streamDescPtr->streamType)
{
case M4SYS_kAMR:
case M4SYS_kAAC:
case M4SYS_kEVRC:
/*Audio*/
ERR_CHECK(streamDescPtr->streamID == AudioStreamID,
M4ERR_PARAMETER);
/*check if an audio track has already been added*/
ERR_CHECK(mMp4FileDataPtr->hasAudio == M4OSA_FALSE,
M4ERR_BAD_CONTEXT);
/*check if alloc need to be done*/
if (mMp4FileDataPtr->audioTrackPtr == M4OSA_NULL)
{
mMp4FileDataPtr->audioTrackPtr = (M4MP4W_AudioTrackData
*)M4OSA_32bitAlignedMalloc(sizeof(M4MP4W_AudioTrackData),
M4MP4_WRITER, (M4OSA_Char *)"M4MP4W_AudioTrackData");
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr != M4OSA_NULL,
M4ERR_ALLOC);
/**
* We must init these pointers in case an alloc bellow fails */
mMp4FileDataPtr->audioTrackPtr->Chunk = M4OSA_NULL;
mMp4FileDataPtr->audioTrackPtr->chunkOffsetTable = M4OSA_NULL;
mMp4FileDataPtr->audioTrackPtr->chunkSizeTable = M4OSA_NULL;
mMp4FileDataPtr->audioTrackPtr->chunkSampleNbTable = M4OSA_NULL;
mMp4FileDataPtr->audioTrackPtr->chunkTimeMsTable = M4OSA_NULL;
mMp4FileDataPtr->audioTrackPtr->TABLE_STTS = M4OSA_NULL;
mMp4FileDataPtr->audioTrackPtr->TABLE_STSZ = M4OSA_NULL;
mMp4FileDataPtr->audioTrackPtr->DSI = M4OSA_NULL;
/*now dynamic*/
#ifdef _M4MP4W_MOOV_FIRST
mMp4FileDataPtr->audioTrackPtr->Chunk =
(M4OSA_UChar ** )M4OSA_32bitAlignedMalloc(M4MP4W_CHUNK_AUDIO_ALLOC_NB
* sizeof(M4OSA_UChar *),
M4MP4_WRITER, (M4OSA_Char *)"audioTrackPtr->Chunk");
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->Chunk != M4OSA_NULL,
M4ERR_ALLOC);
#else
mMp4FileDataPtr->audioTrackPtr->Chunk =
(M4OSA_UChar ** )M4OSA_32bitAlignedMalloc(sizeof(M4OSA_UChar *),
M4MP4_WRITER, (M4OSA_Char *)"audioTrackPtr->Chunk");
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->Chunk != M4OSA_NULL,
M4ERR_ALLOC);
mMp4FileDataPtr->audioTrackPtr->Chunk[0] = M4OSA_NULL;
mMp4FileDataPtr->audioTrackPtr->chunkOffsetTable =
(M4OSA_UInt32 *)M4OSA_32bitAlignedMalloc(M4MP4W_CHUNK_AUDIO_ALLOC_NB
* sizeof(M4OSA_UInt32),
M4MP4_WRITER, (M4OSA_Char *)"audioTrackPtr->chunkOffsetTable");
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->chunkOffsetTable
!= M4OSA_NULL, M4ERR_ALLOC);
#endif /*_M4MP4W_MOOV_FIRST*/
mMp4FileDataPtr->audioTrackPtr->TABLE_STTS =
(M4OSA_UInt32 *)M4OSA_32bitAlignedMalloc(M4MP4W_STTS_AUDIO_ALLOC_SIZE,
M4MP4_WRITER, (M4OSA_Char *)"audioTrackPtr->TABLE_STTS");
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->TABLE_STTS
!= M4OSA_NULL, M4ERR_ALLOC);
mMp4FileDataPtr->audioTrackPtr->nbOfAllocatedSttsBlocks = 1;
mMp4FileDataPtr->audioTrackPtr->chunkSizeTable =
(M4OSA_UInt32 *)M4OSA_32bitAlignedMalloc(M4MP4W_CHUNK_AUDIO_ALLOC_NB
* sizeof(M4OSA_UInt32),
M4MP4_WRITER, (M4OSA_Char *)"audioTrackPtr->chunkSizeTable");
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->chunkSizeTable
!= M4OSA_NULL, M4ERR_ALLOC);
mMp4FileDataPtr->audioTrackPtr->chunkSampleNbTable =
(M4OSA_UInt32 *)M4OSA_32bitAlignedMalloc(M4MP4W_CHUNK_AUDIO_ALLOC_NB
* sizeof(M4OSA_UInt32),
M4MP4_WRITER, (M4OSA_Char *)"audioTrackPtr->chunkSampleNbTable");
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->chunkSampleNbTable
!= M4OSA_NULL, M4ERR_ALLOC);
mMp4FileDataPtr->audioTrackPtr->chunkTimeMsTable =
(M4OSA_UInt32 *)M4OSA_32bitAlignedMalloc(M4MP4W_CHUNK_AUDIO_ALLOC_NB
* sizeof(M4OSA_UInt32),
M4MP4_WRITER, (M4OSA_Char *)"audioTrackPtr->chunkTimeMsTable");
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->chunkTimeMsTable
!= M4OSA_NULL, M4ERR_ALLOC);
mMp4FileDataPtr->audioTrackPtr->LastAllocatedChunk = 0;
}
mMp4FileDataPtr->hasAudio = M4OSA_TRUE;
mMp4FileDataPtr->filesize += 402;
mMp4FileDataPtr->audioTrackPtr->MaxChunkSize =
mMp4FileDataPtr->MaxChunkSize; /* init value */
mMp4FileDataPtr->audioTrackPtr->MaxAUSize =
mMp4FileDataPtr->MaxAUSize;
mMp4FileDataPtr->audioTrackPtr->CommonData.lastCTS = 0;
mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb = 0;
mMp4FileDataPtr->audioTrackPtr->CommonData.sampleSize = 0;
mMp4FileDataPtr->audioTrackPtr->CommonData.sttsTableEntryNb = 1;
mMp4FileDataPtr->audioTrackPtr->CommonData.timescale =
streamDescPtr->timeScale;
mMp4FileDataPtr->audioTrackPtr->chunkSizeTable[0] = 0; /*init*/
mMp4FileDataPtr->audioTrackPtr->chunkSampleNbTable[0] = 0; /*init*/
mMp4FileDataPtr->audioTrackPtr->chunkTimeMsTable[0] = 0; /*init*/
mMp4FileDataPtr->audioTrackPtr->currentChunk =
0; /*1st chunk is Chunk[0]*/
mMp4FileDataPtr->audioTrackPtr->currentPos = 0;
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
mMp4FileDataPtr->audioTrackPtr->currentStsc = 0;
#endif
mMp4FileDataPtr->audioTrackPtr->microState = M4MP4W_ready;
mMp4FileDataPtr->audioTrackPtr->nbOfAllocatedStszBlocks = 0;
mMp4FileDataPtr->audioTrackPtr->TABLE_STSZ = M4OSA_NULL;
mMp4FileDataPtr->audioTrackPtr->avgBitrate =
streamDescPtr->averageBitrate;
mMp4FileDataPtr->audioTrackPtr->maxBitrate =
streamDescPtr->maxBitrate;
if (streamDescPtr->streamType == M4SYS_kAMR)
{
mMp4FileDataPtr->audioTrackPtr->CommonData.trackType =
M4SYS_kAMR;
ERR_CHECK(streamDescPtr->timeScale == 8000, M4ERR_PARAMETER);
mMp4FileDataPtr->audioTrackPtr->sampleDuration =
160; /*AMR8+timescale=8000 => sample duration 160 constant*/
/*Use given DSI if passed, else use default value*/
if (streamDescPtr->decoderSpecificInfoSize != 0)
{
/*amr DSI is 9 bytes long !*/
mMp4FileDataPtr->audioTrackPtr->dsiSize =
9; /*always 9 for amr*/
ERR_CHECK(streamDescPtr->decoderSpecificInfoSize == 9,
M4ERR_PARAMETER);
mMp4FileDataPtr->audioTrackPtr->DSI =
(M4OSA_UChar *)M4OSA_32bitAlignedMalloc(9, M4MP4_WRITER,
(M4OSA_Char *)"audioTrackPtr->DSI");
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->DSI != M4OSA_NULL,
M4ERR_ALLOC);
memcpy(
(void *)mMp4FileDataPtr->audioTrackPtr->DSI,
(void *)streamDescPtr->decoderSpecificInfo,
9);
}
else
{
mMp4FileDataPtr->audioTrackPtr->DSI =
M4OSA_NULL; /*default static block will be used*/
mMp4FileDataPtr->audioTrackPtr->dsiSize =
0; /*but the actual static dsi is 9 bytes !*/
}
}
else if (streamDescPtr->streamType == M4SYS_kEVRC)
{
mMp4FileDataPtr->audioTrackPtr->CommonData.trackType =
M4SYS_kEVRC;
ERR_CHECK(streamDescPtr->timeScale == 8000, M4ERR_PARAMETER);
mMp4FileDataPtr->audioTrackPtr->sampleDuration =
160; /*EVRC+timescale=8000 => sample duration 160 constant*/
/*Use given DSI if passed, else use default value*/
if (streamDescPtr->decoderSpecificInfoSize != 0)
{
/*evrc DSI is 6 bytes long !*/
mMp4FileDataPtr->audioTrackPtr->dsiSize =
6; /*always 6 for evrc*/
ERR_CHECK(streamDescPtr->decoderSpecificInfoSize == 6,
M4ERR_PARAMETER);
mMp4FileDataPtr->audioTrackPtr->DSI =
(M4OSA_UChar *)M4OSA_32bitAlignedMalloc(6, M4MP4_WRITER,
(M4OSA_Char *)"audioTrackPtr->DSI");
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->DSI != M4OSA_NULL,
M4ERR_ALLOC);
memcpy(
(void *)mMp4FileDataPtr->audioTrackPtr->DSI,
(void *)streamDescPtr->decoderSpecificInfo,
6);
}
else
{
mMp4FileDataPtr->audioTrackPtr->DSI =
M4OSA_NULL; /*default static block will be used*/
mMp4FileDataPtr->audioTrackPtr->dsiSize =
0; /*but the actual static dsi is 6 bytes !*/
}
}
else /*M4SYS_kAAC*/
{
/*avg bitrate should be set*/
ERR_CHECK(streamDescPtr->averageBitrate != -1, M4ERR_PARAMETER);
ERR_CHECK(streamDescPtr->maxBitrate != -1, M4ERR_PARAMETER);
mMp4FileDataPtr->audioTrackPtr->CommonData.trackType =
M4SYS_kAAC;
mMp4FileDataPtr->audioTrackPtr->sampleDuration =
0; /*don't know for aac, so set 0*/
mMp4FileDataPtr->audioTrackPtr->dsiSize =
(M4OSA_UInt8)streamDescPtr->decoderSpecificInfoSize;
if (mMp4FileDataPtr->audioTrackPtr->dsiSize != 0)
{
mMp4FileDataPtr->audioTrackPtr->DSI =
(M4OSA_UChar *)M4OSA_32bitAlignedMalloc(
streamDescPtr->decoderSpecificInfoSize,
M4MP4_WRITER, (M4OSA_Char *)"audioTrackPtr->DSI");
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->DSI != M4OSA_NULL,
M4ERR_ALLOC);
memcpy(
(void *)mMp4FileDataPtr->audioTrackPtr->DSI,
(void *)streamDescPtr->decoderSpecificInfo,
streamDescPtr->decoderSpecificInfoSize);
}
else
{
/*no dsi: return bad parameter ?*/
return M4ERR_PARAMETER;
}
}
break;
case (M4SYS_kMPEG_4):
case (M4SYS_kH264):
case (M4SYS_kH263):
/*Video*/
ERR_CHECK(streamDescPtr->streamID == VideoStreamID,
M4ERR_PARAMETER);
/*check if a video track has already been added*/
ERR_CHECK(mMp4FileDataPtr->hasVideo == M4OSA_FALSE,
M4ERR_BAD_CONTEXT);
/*check if alloc need to be done*/
if (mMp4FileDataPtr->videoTrackPtr == M4OSA_NULL)
{
mMp4FileDataPtr->videoTrackPtr = (M4MP4W_VideoTrackData
*)M4OSA_32bitAlignedMalloc(sizeof(M4MP4W_VideoTrackData),
M4MP4_WRITER, (M4OSA_Char *)"M4MP4W_VideoTrackData");
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr != M4OSA_NULL,
M4ERR_ALLOC);
/**
* We must init these pointers in case an alloc bellow fails */
mMp4FileDataPtr->videoTrackPtr->Chunk = M4OSA_NULL;
mMp4FileDataPtr->videoTrackPtr->chunkOffsetTable = M4OSA_NULL;
mMp4FileDataPtr->videoTrackPtr->chunkSizeTable = M4OSA_NULL;
mMp4FileDataPtr->videoTrackPtr->chunkSampleNbTable = M4OSA_NULL;
mMp4FileDataPtr->videoTrackPtr->chunkTimeMsTable = M4OSA_NULL;
mMp4FileDataPtr->videoTrackPtr->TABLE_STTS = M4OSA_NULL;
mMp4FileDataPtr->videoTrackPtr->TABLE_STSZ = M4OSA_NULL;
mMp4FileDataPtr->videoTrackPtr->TABLE_STSS = M4OSA_NULL;
mMp4FileDataPtr->videoTrackPtr->DSI = M4OSA_NULL;
/*now dynamic*/
#ifdef _M4MP4W_MOOV_FIRST
mMp4FileDataPtr->videoTrackPtr->Chunk =
(M4OSA_UChar ** )M4OSA_32bitAlignedMalloc(M4MP4W_CHUNK_ALLOC_NB
* sizeof(M4OSA_UChar *),
M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->Chunk");
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->Chunk != M4OSA_NULL,
M4ERR_ALLOC);
#else
/*re-use the same chunk and flush it when full*/
mMp4FileDataPtr->videoTrackPtr->Chunk =
(M4OSA_UChar ** )M4OSA_32bitAlignedMalloc(sizeof(M4OSA_UChar *),
M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->Chunk");
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->Chunk != M4OSA_NULL,
M4ERR_ALLOC);
mMp4FileDataPtr->videoTrackPtr->Chunk[0] = M4OSA_NULL;
mMp4FileDataPtr->videoTrackPtr->chunkOffsetTable =
(M4OSA_UInt32 *)M4OSA_32bitAlignedMalloc(M4MP4W_CHUNK_ALLOC_NB
* sizeof(M4OSA_UInt32),
M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->chunkOffsetTable");
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->chunkOffsetTable
!= M4OSA_NULL, M4ERR_ALLOC);
#endif /*_M4MP4W_MOOV_FIRST*/
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->Chunk != M4OSA_NULL,
M4ERR_ALLOC);
mMp4FileDataPtr->videoTrackPtr->chunkSizeTable =
(M4OSA_UInt32 *)M4OSA_32bitAlignedMalloc(M4MP4W_CHUNK_ALLOC_NB
* sizeof(M4OSA_UInt32),
M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->chunkSizeTable");
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->chunkSizeTable
!= M4OSA_NULL, M4ERR_ALLOC);
mMp4FileDataPtr->videoTrackPtr->chunkSampleNbTable =
(M4OSA_UInt32 *)M4OSA_32bitAlignedMalloc(M4MP4W_CHUNK_ALLOC_NB
* sizeof(M4OSA_UInt32),
M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->chunkSampleNbTable");
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->chunkSampleNbTable
!= M4OSA_NULL, M4ERR_ALLOC);
mMp4FileDataPtr->videoTrackPtr->chunkTimeMsTable =
(M4MP4W_Time32 *)M4OSA_32bitAlignedMalloc(M4MP4W_CHUNK_ALLOC_NB
* sizeof(M4MP4W_Time32),
M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->chunkTimeMsTable");
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->chunkTimeMsTable
!= M4OSA_NULL, M4ERR_ALLOC);
mMp4FileDataPtr->videoTrackPtr->LastAllocatedChunk = 0;
/*tables are now dynamic*/
mMp4FileDataPtr->videoTrackPtr->TABLE_STTS =
(M4OSA_UInt32 *)M4OSA_32bitAlignedMalloc(M4MP4W_STTS_ALLOC_SIZE,
M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->TABLE_STTS");
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->TABLE_STTS
!= M4OSA_NULL, M4ERR_ALLOC);
mMp4FileDataPtr->videoTrackPtr->nbOfAllocatedSttsBlocks = 1;
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
mMp4FileDataPtr->videoTrackPtr->TABLE_STSZ =
(M4OSA_UInt16 *)M4OSA_32bitAlignedMalloc(M4MP4W_STSZ_ALLOC_SIZE,
M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->TABLE_STSZ");
#else
mMp4FileDataPtr->videoTrackPtr->TABLE_STSZ =
(M4OSA_UInt32 *)M4OSA_32bitAlignedMalloc(M4MP4W_STSZ_ALLOC_SIZE,
M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->TABLE_STSZ");
#endif
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->TABLE_STSZ
!= M4OSA_NULL, M4ERR_ALLOC);
mMp4FileDataPtr->videoTrackPtr->nbOfAllocatedStszBlocks = 1;
mMp4FileDataPtr->videoTrackPtr->TABLE_STSS =
(M4OSA_UInt32 *)M4OSA_32bitAlignedMalloc(M4MP4W_STSS_ALLOC_SIZE,
M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->TABLE_STSS");
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->TABLE_STSS
!= M4OSA_NULL, M4ERR_ALLOC);
mMp4FileDataPtr->videoTrackPtr->nbOfAllocatedStssBlocks = 1;
}
mMp4FileDataPtr->hasVideo = M4OSA_TRUE;
mMp4FileDataPtr->filesize += 462;
mMp4FileDataPtr->videoTrackPtr->width = M4MP4W_DefaultWidth;
mMp4FileDataPtr->videoTrackPtr->height = M4MP4W_DefaultHeight;
mMp4FileDataPtr->videoTrackPtr->MaxAUSize =
mMp4FileDataPtr->MaxAUSize;
mMp4FileDataPtr->videoTrackPtr->CommonData.trackType =
streamDescPtr->streamType;
mMp4FileDataPtr->videoTrackPtr->MaxChunkSize =
mMp4FileDataPtr->MaxChunkSize; /* init value */
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
mMp4FileDataPtr->videoTrackPtr->MaxAUperChunk =
M4MP4W_VIDEO_MAX_AU_PER_CHUNK;
#endif
ERR_CHECK(streamDescPtr->timeScale == 1000, M4ERR_PARAMETER);
mMp4FileDataPtr->videoTrackPtr->CommonData.timescale = 1000;
mMp4FileDataPtr->videoTrackPtr->CommonData.lastCTS = 0;
mMp4FileDataPtr->videoTrackPtr->CommonData.sampleNb = 0;
mMp4FileDataPtr->videoTrackPtr->CommonData.sampleSize = 0;
mMp4FileDataPtr->videoTrackPtr->CommonData.sttsTableEntryNb = 1;
mMp4FileDataPtr->videoTrackPtr->chunkSizeTable[0] = 0; /*init*/
mMp4FileDataPtr->videoTrackPtr->chunkSampleNbTable[0] = 0; /*init*/
mMp4FileDataPtr->videoTrackPtr->chunkTimeMsTable[0] = 0; /*init*/
mMp4FileDataPtr->videoTrackPtr->currentChunk =
0; /*1st chunk is Chunk[0]*/
mMp4FileDataPtr->videoTrackPtr->currentPos = 0;
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
mMp4FileDataPtr->videoTrackPtr->currentStsc = 0;
#endif
mMp4FileDataPtr->videoTrackPtr->stssTableEntryNb = 0;
mMp4FileDataPtr->videoTrackPtr->microState = M4MP4W_ready;
if (streamDescPtr->streamType == M4SYS_kH263)
{
if (( streamDescPtr->averageBitrate == -1)
|| (streamDescPtr->maxBitrate == -1))
{
/*the bitrate will not be written if the bitrate information
is not fully set */
mMp4FileDataPtr->videoTrackPtr->avgBitrate = -1;
mMp4FileDataPtr->videoTrackPtr->maxBitrate = -1;
}
else
{
/*proprietary storage of h263 bitrate.
Warning: not the actual bitrate (bit set to 1).*/
mMp4FileDataPtr->videoTrackPtr->avgBitrate =
streamDescPtr->averageBitrate;
mMp4FileDataPtr->videoTrackPtr->maxBitrate =
streamDescPtr->maxBitrate;
}
if (( 0 != streamDescPtr->decoderSpecificInfoSize)
&& (M4OSA_NULL != streamDescPtr->decoderSpecificInfo))
{
/*decoder specific info size is supposed to be always 7 bytes long */
ERR_CHECK(streamDescPtr->decoderSpecificInfoSize == 7,
M4ERR_PARAMETER);
mMp4FileDataPtr->videoTrackPtr->dsiSize =
(M4OSA_UInt8)streamDescPtr->decoderSpecificInfoSize;
mMp4FileDataPtr->videoTrackPtr->DSI =
(M4OSA_UChar *)M4OSA_32bitAlignedMalloc(
streamDescPtr->decoderSpecificInfoSize,
M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->DSI");
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->DSI != M4OSA_NULL,
M4ERR_ALLOC);
memcpy(
(void *)mMp4FileDataPtr->videoTrackPtr->DSI,
(void *)streamDescPtr->decoderSpecificInfo,
streamDescPtr->decoderSpecificInfoSize);
}
else
{
/*use the default dsi*/
mMp4FileDataPtr->videoTrackPtr->DSI = M4OSA_NULL;
mMp4FileDataPtr->videoTrackPtr->dsiSize = 0;
}
}
if (streamDescPtr->streamType == M4SYS_kMPEG_4)
{
mMp4FileDataPtr->filesize += 22; /*extra bytes (from h263)*/
/* allow DSI to be M4OSA_NULL, in which case the actual DSI will be
set by setOption. */
if (( 0 == streamDescPtr->decoderSpecificInfoSize)
|| (M4OSA_NULL == streamDescPtr->decoderSpecificInfo))
{
mMp4FileDataPtr->videoTrackPtr->DSI = M4OSA_NULL;
mMp4FileDataPtr->videoTrackPtr->dsiSize = 0;
}
else
{
/*MP4V specific*/
/*decoder specific info size is supposed to be always <
105 so that ESD size can be coded with 1 byte*/
/*(this should not be restrictive because dsi is always shorter !)*/
ERR_CHECK(streamDescPtr->decoderSpecificInfoSize < 105,
M4ERR_PARAMETER);
mMp4FileDataPtr->videoTrackPtr->dsiSize =
(M4OSA_UInt8)streamDescPtr->decoderSpecificInfoSize;
mMp4FileDataPtr->videoTrackPtr->DSI =
(M4OSA_UChar *)M4OSA_32bitAlignedMalloc(
streamDescPtr->decoderSpecificInfoSize,
M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->DSI");
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->DSI != M4OSA_NULL,
M4ERR_ALLOC);
memcpy(
(void *)mMp4FileDataPtr->videoTrackPtr->DSI,
(void *)streamDescPtr->decoderSpecificInfo,
streamDescPtr->decoderSpecificInfoSize);
mMp4FileDataPtr->filesize +=
streamDescPtr->decoderSpecificInfoSize;
}
/*avg bitrate should be set*/
ERR_CHECK(streamDescPtr->averageBitrate != -1, M4ERR_PARAMETER);
mMp4FileDataPtr->videoTrackPtr->avgBitrate =
streamDescPtr->averageBitrate;
mMp4FileDataPtr->videoTrackPtr->maxBitrate =
streamDescPtr->averageBitrate;
}
if (streamDescPtr->streamType == M4SYS_kH264)
{
/* H264 specific information */
mMp4FileDataPtr->videoTrackPtr->avgBitrate =
streamDescPtr->averageBitrate;
mMp4FileDataPtr->videoTrackPtr->maxBitrate =
streamDescPtr->maxBitrate;
if ((0 != streamDescPtr->decoderSpecificInfoSize)
&& (M4OSA_NULL != streamDescPtr->decoderSpecificInfo))
{
/* + H.264 trimming */
if (M4OSA_TRUE == mMp4FileDataPtr->bMULPPSSPS)
{
M4OSA_UInt16 SPSLength, PPSLength;
M4OSA_UInt16 *DSI;
/* Store the DSI size */
mMp4FileDataPtr->videoTrackPtr->dsiSize =
(M4OSA_UInt8)streamDescPtr->decoderSpecificInfoSize
- 24;
/* Copy the DSI (SPS + PPS) */
mMp4FileDataPtr->videoTrackPtr->DSI =
(M4OSA_UChar *)M4OSA_32bitAlignedMalloc(
streamDescPtr->decoderSpecificInfoSize,
M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->DSI");
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->DSI
!= M4OSA_NULL, M4ERR_ALLOC);
DSI =
(M4OSA_UInt16 *)streamDescPtr->decoderSpecificInfo;
SPSLength = DSI[6];
PPSLength = DSI[10];
memcpy(
(void *)mMp4FileDataPtr->videoTrackPtr->DSI,
(void *)((streamDescPtr->
decoderSpecificInfo)+12), 2);
memcpy(
(void *)((mMp4FileDataPtr->videoTrackPtr->
DSI)+2), (void *)((streamDescPtr->
decoderSpecificInfo)+28), SPSLength);
memcpy(
(void *)((mMp4FileDataPtr->videoTrackPtr->
DSI)+2 + SPSLength),
(void *)((streamDescPtr->
decoderSpecificInfo)+20), 2);
memcpy(
(void *)((mMp4FileDataPtr->videoTrackPtr->
DSI)+4 + SPSLength),
(void *)((streamDescPtr->
decoderSpecificInfo)+28 + SPSLength),
PPSLength);
/* - H.264 trimming */
}
else
{
/* Store the DSI size */
mMp4FileDataPtr->videoTrackPtr->dsiSize =
(M4OSA_UInt8)streamDescPtr->decoderSpecificInfoSize;
/* Copy the DSI (SPS + PPS) */
mMp4FileDataPtr->videoTrackPtr->DSI =
(M4OSA_UChar *)M4OSA_32bitAlignedMalloc(
streamDescPtr->decoderSpecificInfoSize,
M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->DSI");
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->DSI
!= M4OSA_NULL, M4ERR_ALLOC);
memcpy(
(void *)mMp4FileDataPtr->videoTrackPtr->DSI,
(void *)streamDescPtr->
decoderSpecificInfo,
streamDescPtr->decoderSpecificInfoSize);
}
}
else
{
/*use the default dsi*/
mMp4FileDataPtr->videoTrackPtr->DSI = M4OSA_NULL;
mMp4FileDataPtr->videoTrackPtr->dsiSize = 0;
}
}
break;
default:
err = M4ERR_PARAMETER;
}
return err;
}
/*******************************************************************************/
M4OSA_ERR M4MP4W_startWriting( M4OSA_Context context )
/*******************************************************************************/
{
M4OSA_ERR err = M4NO_ERROR;
M4OSA_UInt32 fileModeAccess = M4OSA_kFileWrite | M4OSA_kFileCreate;
M4OSA_UInt32 i;
M4MP4W_Mp4FileData *mMp4FileDataPtr = (M4MP4W_Mp4FileData *)context;
ERR_CHECK(context != M4OSA_NULL, M4ERR_PARAMETER);
ERR_CHECK((mMp4FileDataPtr->state == M4MP4W_ready), M4ERR_STATE);
mMp4FileDataPtr->state = M4MP4W_writing;
/*audio microstate */
/* if (mMp4FileDataPtr->audioTrackPtr != M4OSA_NULL)*/
if (mMp4FileDataPtr->hasAudio)
{
ERR_CHECK((mMp4FileDataPtr->audioTrackPtr->microState == M4MP4W_ready),
M4ERR_STATE);
mMp4FileDataPtr->audioTrackPtr->microState = M4MP4W_writing;
/* First audio chunk allocation */
mMp4FileDataPtr->audioTrackPtr->Chunk[0] = (M4OSA_UChar
*)M4OSA_32bitAlignedMalloc(mMp4FileDataPtr->audioTrackPtr->MaxChunkSize,
M4MP4_WRITER, (M4OSA_Char *)"audioTrackPtr->Chunk[0]");
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->Chunk[0] != M4OSA_NULL,
M4ERR_ALLOC);
}
/*video microstate*/
/* if (mMp4FileDataPtr->videoTrackPtr != M4OSA_NULL)*/
if (mMp4FileDataPtr->hasVideo)
{
ERR_CHECK((mMp4FileDataPtr->videoTrackPtr->microState == M4MP4W_ready),
M4ERR_STATE);
mMp4FileDataPtr->videoTrackPtr->microState = M4MP4W_writing;
/* First video chunk allocation */
mMp4FileDataPtr->videoTrackPtr->Chunk[0] = (M4OSA_UChar
*)M4OSA_32bitAlignedMalloc(mMp4FileDataPtr->videoTrackPtr->MaxChunkSize,
M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->Chunk[0]");
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->Chunk[0] != M4OSA_NULL,
M4ERR_ALLOC);
}
if (mMp4FileDataPtr->estimateAudioSize == M4OSA_TRUE)
{
/*set audioMsChunkDur (duration in ms before a new chunk is created)
for audio size estimation*/
ERR_CHECK(mMp4FileDataPtr->hasVideo, M4ERR_BAD_CONTEXT);
ERR_CHECK(mMp4FileDataPtr->hasAudio, M4ERR_BAD_CONTEXT);
mMp4FileDataPtr->audioMsChunkDur =
20 * mMp4FileDataPtr->audioTrackPtr->MaxChunkSize
/ mMp4FileDataPtr->audioTrackPtr->MaxAUSize;
if (( mMp4FileDataPtr->InterleaveDur != 0)
&& (mMp4FileDataPtr->InterleaveDur
< 20 *mMp4FileDataPtr->audioTrackPtr->MaxChunkSize
/ mMp4FileDataPtr->audioTrackPtr->MaxAUSize))
{
mMp4FileDataPtr->audioMsChunkDur = mMp4FileDataPtr->InterleaveDur;
}
}
#ifndef _M4MP4W_MOOV_FIRST
/*open file in write binary mode*/
err = mMp4FileDataPtr->fileWriterFunctions->openWrite(
&mMp4FileDataPtr->fileWriterContext,
mMp4FileDataPtr->url, fileModeAccess);
ERR_CHECK((M4NO_ERROR == err), err);
/*ftyp atom*/
if (mMp4FileDataPtr->ftyp.major_brand != 0)
{
/* Put customized ftyp box */
err =
M4MP4W_putBE32(16 + (mMp4FileDataPtr->ftyp.nbCompatibleBrands * 4),
mMp4FileDataPtr->fileWriterFunctions,
mMp4FileDataPtr->fileWriterContext);
ERR_CHECK((M4NO_ERROR == err), err);
err = M4MP4W_putBE32(M4MPAC_FTYP_TAG,
mMp4FileDataPtr->fileWriterFunctions,
mMp4FileDataPtr->fileWriterContext);
ERR_CHECK((M4NO_ERROR == err), err);
err = M4MP4W_putBE32(mMp4FileDataPtr->ftyp.major_brand,
mMp4FileDataPtr->fileWriterFunctions,
mMp4FileDataPtr->fileWriterContext);
ERR_CHECK((M4NO_ERROR == err), err);
err = M4MP4W_putBE32(mMp4FileDataPtr->ftyp.minor_version,
mMp4FileDataPtr->fileWriterFunctions,
mMp4FileDataPtr->fileWriterContext);
ERR_CHECK((M4NO_ERROR == err), err);
for ( i = 0; i < mMp4FileDataPtr->ftyp.nbCompatibleBrands; i++ )
{
err = M4MP4W_putBE32(mMp4FileDataPtr->ftyp.compatible_brands[i],
mMp4FileDataPtr->fileWriterFunctions,
mMp4FileDataPtr->fileWriterContext);
ERR_CHECK((M4NO_ERROR == err), err);
}
}
else
{
/* Put default ftyp box */
err = M4MP4W_putBlock(Default_ftyp, sizeof(Default_ftyp),
mMp4FileDataPtr->fileWriterFunctions,
mMp4FileDataPtr->fileWriterContext);
ERR_CHECK((M4NO_ERROR == err), err);
}
/*init mdat value with 0 but the right value is set just before the file is closed*/
err = M4MP4W_putBE32(0, mMp4FileDataPtr->fileWriterFunctions,
mMp4FileDataPtr->fileWriterContext);
ERR_CHECK((M4NO_ERROR == err), err);
err = M4MP4W_putBlock(CommonBlock2, sizeof(CommonBlock2),
mMp4FileDataPtr->fileWriterFunctions,
mMp4FileDataPtr->fileWriterContext);
ERR_CHECK((M4NO_ERROR == err), err);
#endif /*_M4MP4W_MOOV_FIRST*/
#ifdef _M4MP4W_RESERVED_MOOV_DISK_SPACE
if (0 != mMp4FileDataPtr->MaxFileSize
&& M4OSA_NULL != mMp4FileDataPtr->safetyFileUrl)
{
M4OSA_ERR err2 = M4NO_ERROR;
M4OSA_Context safetyFileContext = M4OSA_NULL;
M4OSA_UInt32 safetyFileSize = 0, addendum = 0;
M4OSA_UChar dummyData[100]; /* To fill the safety file with */
err =
mMp4FileDataPtr->fileWriterFunctions->openWrite(&safetyFileContext,
mMp4FileDataPtr->safetyFileUrl, fileModeAccess);
ERR_CHECK((M4NO_ERROR == err), err);
mMp4FileDataPtr->cleanSafetyFile = M4OSA_TRUE;
/* 10% seems to be a reasonable worst case, but also provision for 1kb of moov overhead.*/
safetyFileSize = 1000 + (mMp4FileDataPtr->MaxFileSize * 10 + 99) / 100;
/* Here we add space to take into account the fact we have to flush any pending
chunk in closeWrite, this space is the sum of the maximum chunk sizes, for each
track. */
#ifndef _M4MP4W_UNBUFFERED_VIDEO
if (mMp4FileDataPtr->hasVideo)
{
safetyFileSize += mMp4FileDataPtr->videoTrackPtr->MaxChunkSize;
}
#endif
if (mMp4FileDataPtr->hasAudio)
{
safetyFileSize += mMp4FileDataPtr->audioTrackPtr->MaxChunkSize;
}
memset((void *)dummyData, 0xCA,sizeof(dummyData)); /* For extra safety. */
for ( i = 0;
i < (safetyFileSize + sizeof(dummyData) - 1) / sizeof(dummyData);
i++ )
{
err = mMp4FileDataPtr->fileWriterFunctions->writeData(
safetyFileContext, dummyData, sizeof(dummyData));
if (M4NO_ERROR != err)
break;
/* Don't return from the function yet, as we need to close the file first. */
}
/* I don't need to keep it open. */
err2 =
mMp4FileDataPtr->fileWriterFunctions->closeWrite(safetyFileContext);
if (M4NO_ERROR != err)
{
return err;
}
else
ERR_CHECK((M4NO_ERROR == err2), err2);
M4OSA_TRACE1_0("Safety file correctly created");
}
#endif /* _M4MP4W_RESERVED_MOOV_DISK_SPACE */
return err;
}
/*******************************************************************************/
M4OSA_ERR M4MP4W_newAudioChunk( M4OSA_Context context,
M4OSA_UInt32 *leftSpaceInChunk )
/*******************************************************************************/
{
M4OSA_ERR err = M4NO_ERROR;
M4MP4W_Mp4FileData *mMp4FileDataPtr = (M4MP4W_Mp4FileData *)context;
M4OSA_Double scale_audio;
#ifndef _M4MP4W_OPTIMIZE_FOR_PHONE
M4OSA_UInt32 reallocNb;
#endif
/* video only */
if (mMp4FileDataPtr->audioTrackPtr == M4OSA_NULL)
return M4NO_ERROR;
M4OSA_TRACE1_0(" M4MP4W_newAudioChunk - flush audio");
M4OSA_TRACE1_2("current chunk = %d offset = 0x%x",
mMp4FileDataPtr->audioTrackPtr->currentChunk,
mMp4FileDataPtr->absoluteCurrentPos);
scale_audio = 1000.0 / mMp4FileDataPtr->audioTrackPtr->CommonData.timescale;
#ifndef _M4MP4W_MOOV_FIRST
/*flush chunk*/
err = M4MP4W_putBlock(mMp4FileDataPtr->audioTrackPtr->Chunk[0],
mMp4FileDataPtr->audioTrackPtr->currentPos,
mMp4FileDataPtr->fileWriterFunctions,
mMp4FileDataPtr->fileWriterContext);
if (M4NO_ERROR != err)
{
M4OSA_FilePosition temp = mMp4FileDataPtr->absoluteCurrentPos;
M4OSA_TRACE2_1(
"M4MP4W_newAudioChunk: putBlock error when flushing chunk: %#X",
err);
/* Ouch, we got an error writing to the file, but we need to properly react so that the
state is still consistent and we can properly close the file so that what has been
recorded so far is not lost. Yay error recovery! */
/* First, we do not know where we are in the file. Put us back at where we were before
attempting to write the data. That way, we're consistent with the chunk state data. */
err = mMp4FileDataPtr->fileWriterFunctions->seek(
mMp4FileDataPtr->fileWriterContext,
M4OSA_kFileSeekBeginning, &temp);
M4OSA_TRACE2_3(
"Backtracking to position 0x%08X, seek returned %d and position %08X",
mMp4FileDataPtr->absoluteCurrentPos, err, temp);
/* Then, do not update any info whatsoever in the writing state. This will have the
consequence that it will be as if the chunk has not been flushed yet, and therefore
it will be done as part of closeWrite (where there could be room to do so,
if some emergency room is freed for that purpose). */
/* And lastly (for here), return that we've reached the limit of available space. */
return M4WAR_MP4W_OVERSIZE;
}
/*update chunk offset*/
mMp4FileDataPtr->audioTrackPtr->
chunkOffsetTable[mMp4FileDataPtr->audioTrackPtr->currentChunk] =
mMp4FileDataPtr->absoluteCurrentPos;
/*add chunk size to absoluteCurrentPos*/
mMp4FileDataPtr->absoluteCurrentPos +=
mMp4FileDataPtr->audioTrackPtr->currentPos;
#endif /*_M4MP4W_MOOV_FIRST*/
/*update chunk info */
mMp4FileDataPtr->audioTrackPtr->
chunkSizeTable[mMp4FileDataPtr->audioTrackPtr->currentChunk] =
mMp4FileDataPtr->audioTrackPtr->currentPos;
mMp4FileDataPtr->audioTrackPtr->
chunkTimeMsTable[mMp4FileDataPtr->audioTrackPtr->currentChunk] =
mMp4FileDataPtr->audioTrackPtr->CommonData.lastCTS;
mMp4FileDataPtr->audioTrackPtr->currentChunk += 1;
/*if audio amount of data is not estimated*/
if (mMp4FileDataPtr->estimateAudioSize == M4OSA_FALSE)
mMp4FileDataPtr->filesize += 16;
/*alloc new chunk*/
/*only if not already allocated*/
if (mMp4FileDataPtr->audioTrackPtr->currentChunk
> mMp4FileDataPtr->audioTrackPtr->LastAllocatedChunk)
{
/*update LastAllocatedChunk ( -> = currentChunk)*/
mMp4FileDataPtr->audioTrackPtr->LastAllocatedChunk += 1;
/*max nb of chunk is now dynamic*/
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
if (mMp4FileDataPtr->audioTrackPtr->LastAllocatedChunk
+ 3 > M4MP4W_CHUNK_AUDIO_ALLOC_NB)
{
M4OSA_TRACE1_0("M4MP4W_newAudioChunk : audio chunk table is full");
return M4WAR_MP4W_OVERSIZE;
}
#else
if (((mMp4FileDataPtr->audioTrackPtr->LastAllocatedChunk)
% M4MP4W_CHUNK_AUDIO_ALLOC_NB) == 0)
{
reallocNb = mMp4FileDataPtr->audioTrackPtr->LastAllocatedChunk
+ M4MP4W_CHUNK_AUDIO_ALLOC_NB;
#ifdef _M4MP4W_MOOV_FIRST
mMp4FileDataPtr->audioTrackPtr->Chunk =
(M4OSA_UChar ** )M4MP4W_realloc(
(M4OSA_MemAddr32)mMp4FileDataPtr->audioTrackPtr->Chunk,
( reallocNb - M4MP4W_CHUNK_AUDIO_ALLOC_NB)
* sizeof(M4OSA_UChar *),
reallocNb * sizeof(M4OSA_UChar *));
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->Chunk != M4OSA_NULL,
M4ERR_ALLOC);
#else
mMp4FileDataPtr->audioTrackPtr->chunkOffsetTable =
(M4OSA_UInt32 *)M4MP4W_realloc(
(M4OSA_MemAddr32)mMp4FileDataPtr->audioTrackPtr->
chunkOffsetTable,
( reallocNb - M4MP4W_CHUNK_AUDIO_ALLOC_NB)
* sizeof(M4OSA_UInt32),
reallocNb * sizeof(M4OSA_UInt32));
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->chunkOffsetTable
!= M4OSA_NULL, M4ERR_ALLOC);
#endif /*_M4MP4W_MOOV_FIRST*/
mMp4FileDataPtr->audioTrackPtr->chunkSizeTable =
(M4OSA_UInt32 *)M4MP4W_realloc(
(M4OSA_MemAddr32)mMp4FileDataPtr->audioTrackPtr->
chunkSizeTable,
( reallocNb - M4MP4W_CHUNK_AUDIO_ALLOC_NB)
* sizeof(M4OSA_UInt32),
reallocNb * sizeof(M4OSA_UInt32));
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->chunkSizeTable
!= M4OSA_NULL, M4ERR_ALLOC);
mMp4FileDataPtr->audioTrackPtr->chunkSampleNbTable =
(M4OSA_UInt32 *)M4MP4W_realloc(
(M4OSA_MemAddr32)mMp4FileDataPtr->audioTrackPtr->
chunkSampleNbTable,
( reallocNb - M4MP4W_CHUNK_AUDIO_ALLOC_NB)
* sizeof(M4OSA_UInt32),
reallocNb * sizeof(M4OSA_UInt32));
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->chunkSampleNbTable
!= M4OSA_NULL, M4ERR_ALLOC);
mMp4FileDataPtr->audioTrackPtr->chunkTimeMsTable =
(M4MP4W_Time32 *)M4MP4W_realloc(
(M4OSA_MemAddr32)mMp4FileDataPtr->audioTrackPtr->
chunkTimeMsTable,
( reallocNb - M4MP4W_CHUNK_AUDIO_ALLOC_NB)
* sizeof(M4MP4W_Time32),
reallocNb * sizeof(M4MP4W_Time32));
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->chunkTimeMsTable
!= M4OSA_NULL, M4ERR_ALLOC);
}
#endif /*_M4MP4W_OPTIMIZE_FOR_PHONE*/
#ifdef _M4MP4W_MOOV_FIRST
mMp4FileDataPtr->audioTrackPtr->
Chunk[mMp4FileDataPtr->audioTrackPtr->currentChunk] = (M4OSA_UChar
*)M4OSA_32bitAlignedMalloc(mMp4FileDataPtr->audioTrackPtr->MaxChunkSize,
M4MP4_WRITER, (M4OSA_Char *)"audioTrackPtr->currentChunk");
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->
Chunk[mMp4FileDataPtr->audioTrackPtr->currentChunk]
!= M4OSA_NULL, M4ERR_ALLOC);
#endif /*_M4MP4W_MOOV_FIRST*/
}
/*update leftSpaceInChunk, currentPos and currentChunkDur*/
*leftSpaceInChunk = mMp4FileDataPtr->audioTrackPtr->MaxChunkSize;
mMp4FileDataPtr->audioTrackPtr->currentPos = 0;
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
/* check wether to use a new stsc or not */
if (mMp4FileDataPtr->audioTrackPtr->currentStsc > 0)
{
if (( mMp4FileDataPtr->audioTrackPtr->
chunkSampleNbTable[mMp4FileDataPtr->audioTrackPtr->
currentStsc] & 0xFFF) != (mMp4FileDataPtr->audioTrackPtr->
chunkSampleNbTable[mMp4FileDataPtr->audioTrackPtr->currentStsc
- 1] & 0xFFF))
mMp4FileDataPtr->audioTrackPtr->currentStsc += 1;
}
else
mMp4FileDataPtr->audioTrackPtr->currentStsc += 1;
/* max nb of chunk is now dynamic */
if (mMp4FileDataPtr->audioTrackPtr->currentStsc
+ 3 > M4MP4W_CHUNK_AUDIO_ALLOC_NB)
{
M4OSA_TRACE1_0("M4MP4W_newAudioChunk : audio stsc table is full");
return M4WAR_MP4W_OVERSIZE;
}
/* set nb of samples in the new chunk to 0 */
mMp4FileDataPtr->audioTrackPtr->
chunkSampleNbTable[mMp4FileDataPtr->audioTrackPtr->currentStsc] =
0 + (mMp4FileDataPtr->audioTrackPtr->currentChunk << 12);
#else
/*set nb of samples in the new chunk to 0*/
mMp4FileDataPtr->audioTrackPtr->
chunkSampleNbTable[mMp4FileDataPtr->audioTrackPtr->currentChunk] = 0;
#endif
/*set time of the new chunk to lastCTS (for initialization, but updated further to the
CTS of the last sample in the chunk)*/
mMp4FileDataPtr->audioTrackPtr->
chunkTimeMsTable[mMp4FileDataPtr->audioTrackPtr->currentChunk] =
(M4OSA_UInt32)(mMp4FileDataPtr->audioTrackPtr->CommonData.lastCTS
* scale_audio);
return err;
}
/*******************************************************************************/
M4OSA_ERR M4MP4W_newVideoChunk( M4OSA_Context context,
M4OSA_UInt32 *leftSpaceInChunk )
/*******************************************************************************/
{
M4OSA_ERR err = M4NO_ERROR;
M4MP4W_Mp4FileData *mMp4FileDataPtr = (M4MP4W_Mp4FileData *)context;
M4OSA_Double scale_video;
#ifndef _M4MP4W_OPTIMIZE_FOR_PHONE
M4OSA_UInt32 reallocNb;
#endif
/* audio only */
if (mMp4FileDataPtr->videoTrackPtr == M4OSA_NULL)
return M4NO_ERROR;
M4OSA_TRACE1_0("M4MP4W_newVideoChunk - flush video");
M4OSA_TRACE1_2("current chunk = %d offset = 0x%x",
mMp4FileDataPtr->videoTrackPtr->currentChunk,
mMp4FileDataPtr->absoluteCurrentPos);
scale_video = 1000.0 / mMp4FileDataPtr->videoTrackPtr->CommonData.timescale;
#ifndef _M4MP4W_MOOV_FIRST
#ifdef _M4MP4W_UNBUFFERED_VIDEO
/* samples are already written to file */
#else
/*flush chunk*/
err = M4MP4W_putBlock(mMp4FileDataPtr->videoTrackPtr->Chunk[0],
mMp4FileDataPtr->videoTrackPtr->currentPos,
mMp4FileDataPtr->fileWriterFunctions,
mMp4FileDataPtr->fileWriterContext);
if (M4NO_ERROR != err)
{
M4OSA_FilePosition temp = mMp4FileDataPtr->absoluteCurrentPos;
M4OSA_TRACE2_1(
"M4MP4W_newVideoChunk: putBlock error when flushing chunk: %#X",
err);
/* Ouch, we got an error writing to the file, but we need to properly react so that the
state is still consistent and we can properly close the file so that what has been
recorded so far is not lost. Yay error recovery! */
/* First, we do not know where we are in the file. Put us back at where we were before
attempting to write the data. That way, we're consistent with the chunk state data. */
err = mMp4FileDataPtr->fileWriterFunctions->seek(
mMp4FileDataPtr->fileWriterContext,
M4OSA_kFileSeekBeginning, &temp);
M4OSA_TRACE2_3(
"Backtracking to position 0x%08X, seek returned %d and position %08X",
mMp4FileDataPtr->absoluteCurrentPos, err, temp);
/* Then, do not update any info whatsoever in the writing state. This will have the
consequence that it will be as if the chunk has not been flushed yet, and therefore it
will be done as part of closeWrite (where there could be room to do so, if some
emergency room is freed for that purpose). */
/* And lastly (for here), return that we've reached the limit of available space.
We don't care about the error originally returned by putBlock. */
return M4WAR_MP4W_OVERSIZE;
}
#endif
/*update chunk offset*/
mMp4FileDataPtr->videoTrackPtr->
chunkOffsetTable[mMp4FileDataPtr->videoTrackPtr->currentChunk] =
mMp4FileDataPtr->absoluteCurrentPos;
/*add chunk size to absoluteCurrentPos*/
mMp4FileDataPtr->absoluteCurrentPos +=
mMp4FileDataPtr->videoTrackPtr->currentPos;
#endif /*_M4MP4W_MOOV_FIRST*/
/*update chunk info before to go for a new one*/
mMp4FileDataPtr->videoTrackPtr->
chunkSizeTable[mMp4FileDataPtr->videoTrackPtr->currentChunk] =
mMp4FileDataPtr->videoTrackPtr->currentPos;
mMp4FileDataPtr->videoTrackPtr->
chunkTimeMsTable[mMp4FileDataPtr->videoTrackPtr->currentChunk] =
(M4OSA_UInt32)(mMp4FileDataPtr->videoTrackPtr->CommonData.lastCTS
* scale_video);
mMp4FileDataPtr->videoTrackPtr->currentChunk += 1;
mMp4FileDataPtr->filesize += 16;
/*alloc new chunk*/
/*only if not already allocated*/
if (mMp4FileDataPtr->videoTrackPtr->currentChunk
> mMp4FileDataPtr->videoTrackPtr->LastAllocatedChunk)
{
/*update LastAllocatedChunk ( -> = currentChunk)*/
mMp4FileDataPtr->videoTrackPtr->LastAllocatedChunk += 1;
/*max nb of chunk is now dynamic*/
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
if ( mMp4FileDataPtr->videoTrackPtr->LastAllocatedChunk
+ 3 > M4MP4W_CHUNK_ALLOC_NB)
{
M4OSA_TRACE1_0("M4MP4W_newVideoChunk : video chunk table is full");
return M4WAR_MP4W_OVERSIZE;
}
#else
if (((mMp4FileDataPtr->videoTrackPtr->LastAllocatedChunk)
% M4MP4W_CHUNK_ALLOC_NB) == 0)
{
reallocNb = mMp4FileDataPtr->videoTrackPtr->LastAllocatedChunk
+ M4MP4W_CHUNK_ALLOC_NB;
#ifdef _M4MP4W_MOOV_FIRST
mMp4FileDataPtr->videoTrackPtr->Chunk =
(M4OSA_UChar ** )M4MP4W_realloc(
(M4OSA_MemAddr32)mMp4FileDataPtr->videoTrackPtr->Chunk,
( reallocNb
- M4MP4W_CHUNK_ALLOC_NB) * sizeof(M4OSA_UChar *),
reallocNb * sizeof(M4OSA_UChar *));
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->Chunk != M4OSA_NULL,
M4ERR_ALLOC);
#else
mMp4FileDataPtr->videoTrackPtr->chunkOffsetTable =
(M4OSA_UInt32 *)M4MP4W_realloc(
(M4OSA_MemAddr32)mMp4FileDataPtr->videoTrackPtr->
chunkOffsetTable, ( reallocNb - M4MP4W_CHUNK_ALLOC_NB)
* sizeof(M4OSA_UInt32),
reallocNb * sizeof(M4OSA_UInt32));
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->chunkOffsetTable
!= M4OSA_NULL, M4ERR_ALLOC);
#endif /*_M4MP4W_MOOV_FIRST*/
mMp4FileDataPtr->videoTrackPtr->chunkSizeTable =
(M4OSA_UInt32 *)M4MP4W_realloc(
(M4OSA_MemAddr32)mMp4FileDataPtr->videoTrackPtr->
chunkSizeTable, ( reallocNb - M4MP4W_CHUNK_ALLOC_NB)
* sizeof(M4OSA_UInt32),
reallocNb * sizeof(M4OSA_UInt32));
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->chunkSizeTable
!= M4OSA_NULL, M4ERR_ALLOC);
mMp4FileDataPtr->videoTrackPtr->chunkSampleNbTable =
(M4OSA_UInt32 *)M4MP4W_realloc(
(M4OSA_MemAddr32)mMp4FileDataPtr->videoTrackPtr->
chunkSampleNbTable, ( reallocNb - M4MP4W_CHUNK_ALLOC_NB)
* sizeof(M4OSA_UInt32),
reallocNb * sizeof(M4OSA_UInt32));
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->chunkSampleNbTable
!= M4OSA_NULL, M4ERR_ALLOC);
mMp4FileDataPtr->videoTrackPtr->chunkTimeMsTable =
(M4MP4W_Time32 *)M4MP4W_realloc(
(M4OSA_MemAddr32)mMp4FileDataPtr->videoTrackPtr->
chunkTimeMsTable, ( reallocNb
- M4MP4W_CHUNK_ALLOC_NB) * sizeof(M4MP4W_Time32),
reallocNb * sizeof(M4MP4W_Time32));
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->chunkTimeMsTable
!= M4OSA_NULL, M4ERR_ALLOC);
}
#endif /*_M4MP4W_OPTIMIZE_FOR_PHONE*/
#ifdef _M4MP4W_MOOV_FIRST
mMp4FileDataPtr->videoTrackPtr->
Chunk[mMp4FileDataPtr->videoTrackPtr->currentChunk] = (M4OSA_UChar
*)M4OSA_32bitAlignedMalloc(mMp4FileDataPtr->videoTrackPtr->MaxChunkSize,
M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->MaxChunkSize");
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->
Chunk[mMp4FileDataPtr->videoTrackPtr->currentChunk]
!= M4OSA_NULL, M4ERR_ALLOC);
#endif /*_M4MP4W_MOOV_FIRST*/
}
/*update leftSpaceInChunk, currentPos and currentChunkDur*/
*leftSpaceInChunk = mMp4FileDataPtr->videoTrackPtr->MaxChunkSize;
mMp4FileDataPtr->videoTrackPtr->currentPos = 0;
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
/* check wether to use a new stsc or not */
if (mMp4FileDataPtr->videoTrackPtr->currentStsc > 0)
{
if ((mMp4FileDataPtr->videoTrackPtr->
chunkSampleNbTable[mMp4FileDataPtr->videoTrackPtr->
currentStsc] & 0xFFF) != (mMp4FileDataPtr->videoTrackPtr->
chunkSampleNbTable[mMp4FileDataPtr->videoTrackPtr->currentStsc
- 1] & 0xFFF))
mMp4FileDataPtr->videoTrackPtr->currentStsc += 1;
}
else
mMp4FileDataPtr->videoTrackPtr->currentStsc += 1;
/* max nb of chunk is now dynamic */
if (mMp4FileDataPtr->videoTrackPtr->currentStsc
+ 3 > M4MP4W_CHUNK_ALLOC_NB)
{
M4OSA_TRACE1_0("M4MP4W_newVideoChunk : video stsc table is full");
return M4WAR_MP4W_OVERSIZE;
}
/* set nb of samples in the new chunk to 0 */
mMp4FileDataPtr->videoTrackPtr->
chunkSampleNbTable[mMp4FileDataPtr->videoTrackPtr->currentStsc] =
0 + (mMp4FileDataPtr->videoTrackPtr->currentChunk << 12);
#else
/*set nb of samples in the new chunk to 0*/
mMp4FileDataPtr->videoTrackPtr->
chunkSampleNbTable[mMp4FileDataPtr->videoTrackPtr->currentChunk] = 0;
#endif
/*set time of the new chunk to lastCTS (for initialization, but updated further to the
CTS of the last sample in the chunk)*/
mMp4FileDataPtr->videoTrackPtr->
chunkTimeMsTable[mMp4FileDataPtr->videoTrackPtr->currentChunk] =
(M4OSA_UInt32)(mMp4FileDataPtr->videoTrackPtr->CommonData.lastCTS
* scale_video);
return err;
}
/*******************************************************************************/
M4OSA_ERR M4MP4W_startAU( M4OSA_Context context, M4SYS_StreamID streamID,
M4SYS_AccessUnit *auPtr )
/*******************************************************************************/
{
M4OSA_ERR err = M4NO_ERROR;
M4MP4W_Mp4FileData *mMp4FileDataPtr = (M4MP4W_Mp4FileData *)context;
M4OSA_UInt32 leftSpaceInChunk;
M4MP4W_Time32 chunkDurMs;
M4OSA_Double scale_audio;
M4OSA_Double scale_video;
ERR_CHECK(context != M4OSA_NULL, M4ERR_PARAMETER);
ERR_CHECK(auPtr != M4OSA_NULL, M4ERR_PARAMETER);
M4OSA_TRACE2_0("----- M4MP4W_startAU -----");
/*check macro state*/
ERR_CHECK((mMp4FileDataPtr->state == M4MP4W_writing), M4ERR_STATE);
if (streamID == AudioStreamID) /*audio stream*/
{
M4OSA_TRACE2_0("M4MP4W_startAU -> audio");
scale_audio =
1000.0 / mMp4FileDataPtr->audioTrackPtr->CommonData.timescale;
/*audio microstate*/
ERR_CHECK((mMp4FileDataPtr->audioTrackPtr->microState
== M4MP4W_writing), M4ERR_STATE);
mMp4FileDataPtr->audioTrackPtr->microState = M4MP4W_writing_startAU;
leftSpaceInChunk = mMp4FileDataPtr->audioTrackPtr->MaxChunkSize
- mMp4FileDataPtr->audioTrackPtr->currentPos;
M4OSA_TRACE2_2("audio %d %d",
mMp4FileDataPtr->audioTrackPtr->currentPos, leftSpaceInChunk);
chunkDurMs =
(M4OSA_UInt32)(( mMp4FileDataPtr->audioTrackPtr->CommonData.lastCTS
* scale_audio) - mMp4FileDataPtr->audioTrackPtr->
chunkTimeMsTable[mMp4FileDataPtr->audioTrackPtr->
currentChunk]);
if ((leftSpaceInChunk < mMp4FileDataPtr->audioTrackPtr->MaxAUSize)
|| (( mMp4FileDataPtr->InterleaveDur != 0)
&& (chunkDurMs >= mMp4FileDataPtr->InterleaveDur)))
{
#ifdef _M4MP4W_UNBUFFERED_VIDEO
/* only if there is at least 1 video sample in the chunk */
if ((mMp4FileDataPtr->videoTrackPtr != M4OSA_NULL)
&& (mMp4FileDataPtr->videoTrackPtr->currentPos > 0))
{
/* close the opened video chunk before creating a new audio one */
err = M4MP4W_newVideoChunk(context, &leftSpaceInChunk);
if (err != M4NO_ERROR)
return err;
}
#endif
/* not enough space in current chunk: create a new one */
err = M4MP4W_newAudioChunk(context, &leftSpaceInChunk);
if (err != M4NO_ERROR)
return err;
}
auPtr->size = leftSpaceInChunk;
#ifdef _M4MP4W_MOOV_FIRST
auPtr->dataAddress = (M4OSA_MemAddr32)(mMp4FileDataPtr->audioTrackPtr->
Chunk[mMp4FileDataPtr->audioTrackPtr->currentChunk]
+ mMp4FileDataPtr->audioTrackPtr->currentPos);
#else
auPtr->dataAddress =
(M4OSA_MemAddr32)(mMp4FileDataPtr->audioTrackPtr->Chunk[0]
+ mMp4FileDataPtr->audioTrackPtr->currentPos);
#endif /*_M4MP4W_MOOV_FIRST*/
}
else if (streamID == VideoStreamID) /*video stream*/
{
M4OSA_TRACE2_0("M4MP4W_startAU -> video");
scale_video =
1000.0 / mMp4FileDataPtr->videoTrackPtr->CommonData.timescale;
/*video microstate*/
ERR_CHECK((mMp4FileDataPtr->videoTrackPtr->microState
== M4MP4W_writing), M4ERR_STATE);
mMp4FileDataPtr->videoTrackPtr->microState = M4MP4W_writing_startAU;
leftSpaceInChunk = mMp4FileDataPtr->videoTrackPtr->MaxChunkSize
- mMp4FileDataPtr->videoTrackPtr->currentPos;
chunkDurMs =
(M4OSA_UInt32)(( mMp4FileDataPtr->videoTrackPtr->CommonData.lastCTS
* scale_video) - mMp4FileDataPtr->videoTrackPtr->
chunkTimeMsTable[mMp4FileDataPtr->videoTrackPtr->
currentChunk]);
#ifdef _M4MP4W_UNBUFFERED_VIDEO
leftSpaceInChunk = mMp4FileDataPtr->videoTrackPtr->MaxChunkSize;
#endif
M4OSA_TRACE2_2("video %d %d",
mMp4FileDataPtr->videoTrackPtr->currentPos, leftSpaceInChunk);
if (( leftSpaceInChunk < mMp4FileDataPtr->videoTrackPtr->MaxAUSize)
|| (( mMp4FileDataPtr->InterleaveDur != 0)
&& (chunkDurMs >= mMp4FileDataPtr->InterleaveDur))
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
|| (( mMp4FileDataPtr->videoTrackPtr->MaxAUperChunk != 0)
&& (( mMp4FileDataPtr->videoTrackPtr->
chunkSampleNbTable[mMp4FileDataPtr->videoTrackPtr->
currentStsc] & 0xFFF)
== mMp4FileDataPtr->videoTrackPtr->MaxAUperChunk))
#endif
)
{
/*not enough space in current chunk: create a new one*/
err = M4MP4W_newVideoChunk(context, &leftSpaceInChunk);
if (err != M4NO_ERROR)
return err;
}
M4OSA_TRACE2_3("startAU: size 0x%x pos 0x%x chunk %u", auPtr->size,
mMp4FileDataPtr->videoTrackPtr->currentPos,
mMp4FileDataPtr->videoTrackPtr->currentChunk);
M4OSA_TRACE3_1("adr = 0x%p", auPtr->dataAddress);
if (auPtr->dataAddress)
{
M4OSA_TRACE3_3(" data = %08X %08X %08X", auPtr->dataAddress[0],
auPtr->dataAddress[1], auPtr->dataAddress[2]);
}
auPtr->size = leftSpaceInChunk;
#ifdef _M4MP4W_MOOV_FIRST
if (mMp4FileDataPtr->videoTrackPtr->CommonData.trackType
== M4SYS_kH264)
auPtr->dataAddress =
(M4OSA_MemAddr32)(mMp4FileDataPtr->videoTrackPtr->
Chunk[mMp4FileDataPtr->videoTrackPtr->currentChunk]
+ mMp4FileDataPtr->videoTrackPtr->currentPos + 4);
else
auPtr->dataAddress =
(M4OSA_MemAddr32)(mMp4FileDataPtr->videoTrackPtr->
Chunk[mMp4FileDataPtr->videoTrackPtr->currentChunk]
+ mMp4FileDataPtr->videoTrackPtr->currentPos);
#else
#ifdef _M4MP4W_UNBUFFERED_VIDEO
if (mMp4FileDataPtr->videoTrackPtr->CommonData.trackType
== M4SYS_kH264)
auPtr->dataAddress =
(M4OSA_MemAddr32)(mMp4FileDataPtr->videoTrackPtr->Chunk[0] + 4);
else
auPtr->dataAddress =
(M4OSA_MemAddr32)(mMp4FileDataPtr->videoTrackPtr->Chunk[0]);
#else
if (mMp4FileDataPtr->videoTrackPtr->CommonData.trackType
== M4SYS_kH264)
auPtr->dataAddress =
(M4OSA_MemAddr32)(mMp4FileDataPtr->videoTrackPtr->Chunk[0]
+ mMp4FileDataPtr->videoTrackPtr->currentPos
+ 4); /* In H264, we must start by the length of the NALU, coded in 4 bytes */
else
auPtr->dataAddress =
(M4OSA_MemAddr32)(mMp4FileDataPtr->videoTrackPtr->Chunk[0]
+ mMp4FileDataPtr->videoTrackPtr->currentPos);
#endif /*_M4MP4W_UNBUFFERED_VIDEO*/
#endif /*_M4MP4W_MOOV_FIRST*/
}
else
return M4ERR_BAD_STREAM_ID;
M4OSA_TRACE1_3("M4MPW_startAU: start address:%p, size:%lu, stream:%d",
auPtr->dataAddress, auPtr->size, streamID);
return err;
}
/*******************************************************************************/
M4OSA_ERR M4MP4W_processAU( M4OSA_Context context, M4SYS_StreamID streamID,
M4SYS_AccessUnit *auPtr )
/*******************************************************************************/
{
M4OSA_ERR err = M4NO_ERROR;
M4MP4W_Time32 delta;
M4MP4W_Time32 lastSampleDur;
M4OSA_UInt32 i;
/*expectedSize is the max filesize to forecast when adding a new AU:*/
M4OSA_UInt32 expectedSize =
32; /*initialized with an estimation of the max metadata space needed for an AU.*/
M4OSA_Double scale_audio = 0.0;
M4OSA_Double scale_video = 0.0;
M4MP4W_Mp4FileData *mMp4FileDataPtr = (M4MP4W_Mp4FileData *)context;
ERR_CHECK(context != M4OSA_NULL, M4ERR_PARAMETER);
/*check macro state*/
ERR_CHECK((mMp4FileDataPtr->state == M4MP4W_writing), M4ERR_STATE);
M4OSA_TRACE2_0("M4MP4W_processAU");
if (streamID == AudioStreamID)
scale_audio =
1000.0 / mMp4FileDataPtr->audioTrackPtr->CommonData.timescale;
if (streamID == VideoStreamID)
scale_video =
1000.0 / mMp4FileDataPtr->videoTrackPtr->CommonData.timescale;
/* PL 27/10/2008: after the resurgence of the AAC 128 bug, I added a debug check that
the encoded data didn't overflow the available space in the AU */
switch( streamID )
{
case AudioStreamID:
M4OSA_DEBUG_IF1(auPtr->size
+ mMp4FileDataPtr->audioTrackPtr->currentPos
> mMp4FileDataPtr->audioTrackPtr->MaxChunkSize,
M4ERR_CONTEXT_FAILED,
"Uh oh. Buffer overflow in the writer. Abandon ship!");
M4OSA_DEBUG_IF2(auPtr->size
> mMp4FileDataPtr->audioTrackPtr->MaxAUSize,
M4ERR_CONTEXT_FAILED,
"Oops. An AU went over the declared Max AU size.\
You might wish to investigate that.");
break;
case VideoStreamID:
M4OSA_DEBUG_IF1(auPtr->size
+ mMp4FileDataPtr->videoTrackPtr->currentPos
> mMp4FileDataPtr->videoTrackPtr->MaxChunkSize,
M4ERR_CONTEXT_FAILED,
"Uh oh. Buffer overflow in the writer. Abandon ship!");
M4OSA_DEBUG_IF2(auPtr->size
> mMp4FileDataPtr->videoTrackPtr->MaxAUSize,
M4ERR_CONTEXT_FAILED,
"Oops. An AU went over the declared Max AU size.\
You might wish to investigate that.");
break;
}
/*only if not in the case audio with estimateAudioSize
(else, size already estimated at this point)*/
if ((mMp4FileDataPtr->estimateAudioSize == M4OSA_FALSE)
|| (streamID == VideoStreamID))
{
/*check filesize if needed*/
if (mMp4FileDataPtr->MaxFileSize != 0)
{
expectedSize += mMp4FileDataPtr->filesize + auPtr->size;
if ((streamID == VideoStreamID)
&& (mMp4FileDataPtr->videoTrackPtr->CommonData.trackType
== M4SYS_kH264))
{
expectedSize += 4;
}
if (expectedSize > mMp4FileDataPtr->MaxFileSize)
{
M4OSA_TRACE1_0("processAU : !! FILESIZE EXCEEDED !!");
/* patch for autostop is MaxFileSize exceeded */
M4OSA_TRACE1_0("M4MP4W_processAU : stop at targeted filesize");
return M4WAR_MP4W_OVERSIZE;
}
}
}
/*case audioMsStopTime has already been set during video processing,
and now check it for audio*/
if ((mMp4FileDataPtr->estimateAudioSize == M4OSA_TRUE)
&& (streamID == AudioStreamID))
{
if (mMp4FileDataPtr->audioMsStopTime <= (auPtr->CTS *scale_audio))
{
/* bugfix: if a new chunk was just created, cancel it before to close */
if ((mMp4FileDataPtr->audioTrackPtr->currentChunk != 0)
&& (mMp4FileDataPtr->audioTrackPtr->currentPos == 0))
{
mMp4FileDataPtr->audioTrackPtr->currentChunk--;
}
M4OSA_TRACE1_0("M4MP4W_processAU : audio stop time reached");
return M4WAR_MP4W_OVERSIZE;
}
}
if (streamID == AudioStreamID) /*audio stream*/
{
M4OSA_TRACE2_0("M4MP4W_processAU -> audio");
/*audio microstate*/
ERR_CHECK((mMp4FileDataPtr->audioTrackPtr->microState
== M4MP4W_writing_startAU), M4ERR_STATE);
mMp4FileDataPtr->audioTrackPtr->microState = M4MP4W_writing;
mMp4FileDataPtr->audioTrackPtr->currentPos += auPtr->size;
/* Warning: time conversion cast 64to32! */
delta = (M4MP4W_Time32)auPtr->CTS
- mMp4FileDataPtr->audioTrackPtr->CommonData.lastCTS;
/* DEBUG stts entries which are equal to 0 */
M4OSA_TRACE2_1("A_DELTA = %ld\n", delta);
if (mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb
== 0) /*test if first AU*/
{
/*set au size*/
mMp4FileDataPtr->audioTrackPtr->CommonData.sampleSize = auPtr->size;
/*sample duration is a priori constant in audio case, */
/*but if an Au at least has different size, a stsz table will be created */
/*mMp4FileDataPtr->audioTrackPtr->sampleDuration = delta; */
/*TODO test sample duration? (should be 20ms in AMR8, 160 tics with timescale 8000) */
}
else
{
/*check if au size is constant (audio) */
/*0 sample size means non constant size*/
if (mMp4FileDataPtr->audioTrackPtr->CommonData.sampleSize != 0)
{
if (mMp4FileDataPtr->audioTrackPtr->CommonData.sampleSize
!= auPtr->size)
{
/*first AU with different size => non constant size => STSZ table needed*/
/*computation of the nb of block of size M4MP4W_STSZ_ALLOC_SIZE to allocate*/
mMp4FileDataPtr->audioTrackPtr->nbOfAllocatedStszBlocks =
1 + mMp4FileDataPtr->audioTrackPtr->
CommonData.sampleNb
* 4 / M4MP4W_STSZ_AUDIO_ALLOC_SIZE;
mMp4FileDataPtr->audioTrackPtr->TABLE_STSZ =
(M4OSA_UInt32 *)M4OSA_32bitAlignedMalloc(
mMp4FileDataPtr->audioTrackPtr->
nbOfAllocatedStszBlocks
* M4MP4W_STSZ_AUDIO_ALLOC_SIZE,
M4MP4_WRITER, (M4OSA_Char *)"audioTrackPtr->TABLE_STSZ");
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->TABLE_STSZ
!= M4OSA_NULL, M4ERR_ALLOC);
for ( i = 0;
i < mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb;
i++ )
{
mMp4FileDataPtr->audioTrackPtr->TABLE_STSZ[i] =
mMp4FileDataPtr->audioTrackPtr->
CommonData.sampleSize;
}
mMp4FileDataPtr->audioTrackPtr->
TABLE_STSZ[mMp4FileDataPtr->audioTrackPtr->
CommonData.sampleNb] = auPtr->size;
mMp4FileDataPtr->audioTrackPtr->CommonData.sampleSize =
0; /*used as a flag in that case*/
/*more bytes in the file in that case:*/
if (mMp4FileDataPtr->estimateAudioSize == M4OSA_FALSE)
mMp4FileDataPtr->filesize +=
4 * mMp4FileDataPtr->audioTrackPtr->
CommonData.sampleNb;
}
}
/*else table already exists*/
else
{
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
if (4 *(mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb + 3)
>= mMp4FileDataPtr->audioTrackPtr->nbOfAllocatedStszBlocks
*M4MP4W_STSZ_AUDIO_ALLOC_SIZE)
{
M4OSA_TRACE1_0(
"M4MP4W_processAU : audio stsz table is full");
return M4WAR_MP4W_OVERSIZE;
}
#else
if (4 *mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb
>= mMp4FileDataPtr->audioTrackPtr->nbOfAllocatedStszBlocks
*M4MP4W_STSZ_AUDIO_ALLOC_SIZE)
{
mMp4FileDataPtr->audioTrackPtr->nbOfAllocatedStszBlocks +=
1;
mMp4FileDataPtr->audioTrackPtr->TABLE_STSZ =
(M4OSA_UInt32 *)M4MP4W_realloc(
(M4OSA_MemAddr32)mMp4FileDataPtr->audioTrackPtr->
TABLE_STSZ, ( mMp4FileDataPtr->audioTrackPtr->
nbOfAllocatedStszBlocks - 1)
* M4MP4W_STSZ_AUDIO_ALLOC_SIZE,
mMp4FileDataPtr->audioTrackPtr->
nbOfAllocatedStszBlocks
* M4MP4W_STSZ_AUDIO_ALLOC_SIZE);
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->TABLE_STSZ
!= M4OSA_NULL, M4ERR_ALLOC);
}
#endif /*_M4MP4W_OPTIMIZE_FOR_PHONE*/
mMp4FileDataPtr->audioTrackPtr->
TABLE_STSZ[mMp4FileDataPtr->audioTrackPtr->
CommonData.sampleNb] = auPtr->size;
if (mMp4FileDataPtr->estimateAudioSize == M4OSA_FALSE)
mMp4FileDataPtr->filesize += 4;
}
}
if (delta > mMp4FileDataPtr->audioTrackPtr->sampleDuration)
{
/* keep track of real sample duration*/
mMp4FileDataPtr->audioTrackPtr->sampleDuration = delta;
}
if (mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb
== 0) /*test if first AU*/
{
mMp4FileDataPtr->audioTrackPtr->TABLE_STTS[0] = 1;
mMp4FileDataPtr->audioTrackPtr->TABLE_STTS[1] = 0;
mMp4FileDataPtr->audioTrackPtr->CommonData.sttsTableEntryNb = 1;
mMp4FileDataPtr->filesize += 8;
}
else if (mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb
== 1) /*test if second AU*/
{
#ifndef DUPLICATE_STTS_IN_LAST_AU
mMp4FileDataPtr->audioTrackPtr->TABLE_STTS[0] += 1;
#endif /*DUPLICATE_STTS_IN_LAST_AU*/
mMp4FileDataPtr->audioTrackPtr->TABLE_STTS[1] = delta;
mMp4FileDataPtr->audioTrackPtr->CommonData.sttsTableEntryNb += 1;
mMp4FileDataPtr->filesize += 8;
}
else
{
/*retrieve last sample delta*/
lastSampleDur = mMp4FileDataPtr->audioTrackPtr->TABLE_STTS[2
* (mMp4FileDataPtr->audioTrackPtr->
CommonData.sttsTableEntryNb - 1) - 1];
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
if (8 *(mMp4FileDataPtr->audioTrackPtr->CommonData.sttsTableEntryNb
+ 3) >= mMp4FileDataPtr->audioTrackPtr->nbOfAllocatedSttsBlocks
*M4MP4W_STTS_AUDIO_ALLOC_SIZE)
{
M4OSA_TRACE1_0("M4MP4W_processAU : audio stts table is full");
return M4WAR_MP4W_OVERSIZE;
}
#else
if (8 *mMp4FileDataPtr->audioTrackPtr->CommonData.sttsTableEntryNb
>= mMp4FileDataPtr->audioTrackPtr->nbOfAllocatedSttsBlocks
*M4MP4W_STTS_AUDIO_ALLOC_SIZE)
{
mMp4FileDataPtr->audioTrackPtr->nbOfAllocatedSttsBlocks += 1;
mMp4FileDataPtr->audioTrackPtr->TABLE_STTS =
(M4OSA_UInt32 *)M4MP4W_realloc(
(M4OSA_MemAddr32)mMp4FileDataPtr->audioTrackPtr->
TABLE_STTS, ( mMp4FileDataPtr->audioTrackPtr->
nbOfAllocatedSttsBlocks
- 1) * M4MP4W_STTS_AUDIO_ALLOC_SIZE,
mMp4FileDataPtr->audioTrackPtr->
nbOfAllocatedSttsBlocks
* M4MP4W_STTS_AUDIO_ALLOC_SIZE);
ERR_CHECK(mMp4FileDataPtr->audioTrackPtr->TABLE_STTS
!= M4OSA_NULL, M4ERR_ALLOC);
}
#endif /*_M4MP4W_OPTIMIZE_FOR_PHONE*/
if (delta != lastSampleDur) /*new entry in the table*/
{
mMp4FileDataPtr->audioTrackPtr->TABLE_STTS[2 *(
mMp4FileDataPtr->audioTrackPtr->
CommonData.sttsTableEntryNb - 1)] = 1;
mMp4FileDataPtr->audioTrackPtr->TABLE_STTS[2 *(
mMp4FileDataPtr->audioTrackPtr->
CommonData.sttsTableEntryNb - 1) + 1] = delta;
mMp4FileDataPtr->audioTrackPtr->CommonData.sttsTableEntryNb +=
1;
mMp4FileDataPtr->filesize += 8;
}
else
{
/*increase of 1 the number of consecutive AUs with same duration*/
mMp4FileDataPtr->audioTrackPtr->TABLE_STTS[2 *(
mMp4FileDataPtr->audioTrackPtr->
CommonData.sttsTableEntryNb - 1) - 2] += 1;
}
}
mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb += 1;
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
mMp4FileDataPtr->audioTrackPtr->
chunkSampleNbTable[mMp4FileDataPtr->audioTrackPtr->currentStsc] +=
1;
#else
mMp4FileDataPtr->audioTrackPtr->
chunkSampleNbTable[mMp4FileDataPtr->audioTrackPtr->currentChunk] +=
1;
#endif
/* Warning: time conversion cast 64to32! */
mMp4FileDataPtr->audioTrackPtr->CommonData.lastCTS =
(M4MP4W_Time32)auPtr->CTS;
}
else if (streamID == VideoStreamID) /*video stream*/
{
M4OSA_TRACE2_0("M4MP4W_processAU -> video");
/* In h264, the size of the AU must be added to the data */
if (mMp4FileDataPtr->videoTrackPtr->CommonData.trackType
== M4SYS_kH264)
{
/* Add the size of the NALU in BE */
M4OSA_MemAddr8 pTmpDataAddress = M4OSA_NULL;
auPtr->dataAddress -= 1;
pTmpDataAddress = (M4OSA_MemAddr8)auPtr->dataAddress;
// bit manipulation
*pTmpDataAddress++ = (M4OSA_UInt8)((auPtr->size >> 24) & 0x000000FF);
*pTmpDataAddress++ = (M4OSA_UInt8)((auPtr->size >> 16) & 0x000000FF);
*pTmpDataAddress++ = (M4OSA_UInt8)((auPtr->size >> 8) & 0x000000FF);
*pTmpDataAddress++ = (M4OSA_UInt8)((auPtr->size) & 0x000000FF);
auPtr->size += 4;
}
/*video microstate*/
ERR_CHECK((mMp4FileDataPtr->videoTrackPtr->microState
== M4MP4W_writing_startAU), M4ERR_STATE);
mMp4FileDataPtr->videoTrackPtr->microState = M4MP4W_writing;
#ifdef _M4MP4W_UNBUFFERED_VIDEO
/* samples are written to file now */
err = M4MP4W_putBlock((M4OSA_UChar *)auPtr->dataAddress, auPtr->size,
mMp4FileDataPtr->fileWriterFunctions,
mMp4FileDataPtr->fileWriterContext);
if (err != M4NO_ERROR)
{
M4OSA_FilePosition temp = mMp4FileDataPtr->absoluteCurrentPos
+ mMp4FileDataPtr->videoTrackPtr->currentPos;
M4OSA_TRACE2_1(
"M4MP4W_processAU: putBlock error when writing unbuffered video sample: %#X",
err);
/* Ouch, we got an error writing to the file, but we need to properly react so that
the state is still consistent and we can properly close the file so that what has
been recorded so far is not lost. Yay error recovery! */
/* First, we do not know where we are in the file. Put us back at where we were before
attempting to write the data. That way, we're consistent with the chunk and sample
state data.absoluteCurrentPos is only updated for chunks, it points to the beginning
of the chunk,therefore we need to add videoTrackPtr->currentPos to know where we
were in the file. */
err = mMp4FileDataPtr->fileWriterFunctions->seek(
mMp4FileDataPtr->fileWriterContext,
M4OSA_kFileSeekBeginning, &temp);
M4OSA_TRACE2_3(
"Backtracking to position 0x%08X, seek returned %d and position %08X",
mMp4FileDataPtr->absoluteCurrentPos
+ mMp4FileDataPtr->videoTrackPtr->currentPos, err, temp);
/* Then, do not update any info whatsoever in the writing state. This will have the
consequence that it will be as if the sample has never been written, so the chunk
will be merely closed after the previous sample (the sample we attempted to write
here is lost). */
/* And lastly (for here), return that we've reached the limit of available space.
We don't care about the error originally returned by putBlock. */
return M4WAR_MP4W_OVERSIZE;
}
#endif
if ((M4MP4W_Time32)auPtr->CTS < mMp4FileDataPtr->videoTrackPtr->CommonData.lastCTS) {
// Do not report as error, it will abort the entire filewrite. Just skip this frame.
M4OSA_TRACE1_0("Skip frame. Video frame has too old timestamp.");
return M4NO_ERROR;
}
mMp4FileDataPtr->videoTrackPtr->currentPos += auPtr->size;
/* Warning: time conversion cast 64to32! */
delta = (M4MP4W_Time32)auPtr->CTS
- mMp4FileDataPtr->videoTrackPtr->CommonData.lastCTS;
/* DEBUG stts entries which are equal to 0 */
M4OSA_TRACE2_1("V_DELTA = %ld\n", delta);
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
if (2 *(mMp4FileDataPtr->videoTrackPtr->CommonData.sampleNb + 3)
>= mMp4FileDataPtr->videoTrackPtr->nbOfAllocatedStszBlocks
*M4MP4W_STSZ_ALLOC_SIZE)
{
M4OSA_TRACE1_0("M4MP4W_processAU : video stsz table is full");
return M4WAR_MP4W_OVERSIZE;
}
mMp4FileDataPtr->videoTrackPtr->
TABLE_STSZ[mMp4FileDataPtr->videoTrackPtr->CommonData.sampleNb] =
(M4OSA_UInt16)auPtr->size;
mMp4FileDataPtr->filesize += 4;
#else
if (4 *mMp4FileDataPtr->videoTrackPtr->CommonData.sampleNb
>= mMp4FileDataPtr->videoTrackPtr->nbOfAllocatedStszBlocks
*M4MP4W_STSZ_ALLOC_SIZE)
{
mMp4FileDataPtr->videoTrackPtr->nbOfAllocatedStszBlocks += 1;
mMp4FileDataPtr->videoTrackPtr->TABLE_STSZ =
(M4OSA_UInt32 *)M4MP4W_realloc(
(M4OSA_MemAddr32)mMp4FileDataPtr->videoTrackPtr->TABLE_STSZ,
( mMp4FileDataPtr->videoTrackPtr->
nbOfAllocatedStszBlocks
- 1) * M4MP4W_STSZ_ALLOC_SIZE,
mMp4FileDataPtr->videoTrackPtr->nbOfAllocatedStszBlocks
* M4MP4W_STSZ_ALLOC_SIZE);
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->TABLE_STSZ != M4OSA_NULL,
M4ERR_ALLOC);
}
mMp4FileDataPtr->videoTrackPtr->
TABLE_STSZ[mMp4FileDataPtr->videoTrackPtr->CommonData.sampleNb] =
auPtr->size;
mMp4FileDataPtr->filesize += 4;
#endif
if (mMp4FileDataPtr->videoTrackPtr->CommonData.sampleNb
== 0) /*test if first AU*/
{
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
M4MP4W_put32_Lo(&mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[0], 1);
M4MP4W_put32_Hi(&mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[0], 0);
#else
mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[0] = 1;
mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[1] = 0;
#endif
mMp4FileDataPtr->videoTrackPtr->CommonData.sttsTableEntryNb = 1;
mMp4FileDataPtr->filesize += 8;
}
else if (mMp4FileDataPtr->videoTrackPtr->CommonData.sampleNb
== 1 ) /*test if second AU*/
{
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
M4MP4W_put32_Hi(&mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[0],
(M4OSA_UInt16)delta);
#else
mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[1] = delta;
#endif
}
else
{
/*retrieve last sample delta*/
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
lastSampleDur = M4MP4W_get32_Hi(&mMp4FileDataPtr->videoTrackPtr->
TABLE_STTS[mMp4FileDataPtr->videoTrackPtr->
CommonData.sttsTableEntryNb - 1]);
if (4 *(mMp4FileDataPtr->videoTrackPtr->CommonData.sttsTableEntryNb
+ 3) >= mMp4FileDataPtr->videoTrackPtr->nbOfAllocatedSttsBlocks
*M4MP4W_STTS_ALLOC_SIZE)
{
M4OSA_TRACE1_0("M4MP4W_processAU : video stts table is full");
return M4WAR_MP4W_OVERSIZE;
}
#else
lastSampleDur = mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[2
* (mMp4FileDataPtr->videoTrackPtr->
CommonData.sttsTableEntryNb - 1) + 1];
if (8 *mMp4FileDataPtr->videoTrackPtr->CommonData.sttsTableEntryNb
>= mMp4FileDataPtr->videoTrackPtr->nbOfAllocatedSttsBlocks
*M4MP4W_STTS_ALLOC_SIZE)
{
mMp4FileDataPtr->videoTrackPtr->nbOfAllocatedSttsBlocks += 1;
mMp4FileDataPtr->videoTrackPtr->TABLE_STTS =
(M4OSA_UInt32 *)M4MP4W_realloc(
(M4OSA_MemAddr32)mMp4FileDataPtr->videoTrackPtr->
TABLE_STTS, ( mMp4FileDataPtr->videoTrackPtr->
nbOfAllocatedSttsBlocks
- 1) * M4MP4W_STTS_ALLOC_SIZE,
mMp4FileDataPtr->videoTrackPtr->
nbOfAllocatedSttsBlocks
* M4MP4W_STTS_ALLOC_SIZE);
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->TABLE_STTS
!= M4OSA_NULL, M4ERR_ALLOC);
}
#endif /*_M4MP4W_OPTIMIZE_FOR_PHONE*/
if (delta != lastSampleDur) /*new entry in the table*/
{
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
M4MP4W_put32_Lo(&mMp4FileDataPtr->videoTrackPtr->
TABLE_STTS[mMp4FileDataPtr->videoTrackPtr->
CommonData.sttsTableEntryNb], 1);
M4MP4W_put32_Hi(&mMp4FileDataPtr->videoTrackPtr->
TABLE_STTS[mMp4FileDataPtr->videoTrackPtr->
CommonData.sttsTableEntryNb], (M4OSA_UInt16)delta);
#else
mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[2 *(
mMp4FileDataPtr->videoTrackPtr->
CommonData.sttsTableEntryNb)] = 1;
mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[2
*(mMp4FileDataPtr->videoTrackPtr->
CommonData.sttsTableEntryNb)+1] = delta;
#endif
mMp4FileDataPtr->videoTrackPtr->CommonData.sttsTableEntryNb +=
1;
mMp4FileDataPtr->filesize += 8;
}
else
{
/*increase of 1 the number of consecutive AUs with same duration*/
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
mMp4FileDataPtr->videoTrackPtr->
TABLE_STTS[mMp4FileDataPtr->videoTrackPtr->
CommonData.sttsTableEntryNb - 1] += 1;
#else
mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[2 *(
mMp4FileDataPtr->videoTrackPtr->
CommonData.sttsTableEntryNb - 1)] += 1;
#endif
}
}
mMp4FileDataPtr->videoTrackPtr->CommonData.sampleNb += 1;
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
mMp4FileDataPtr->videoTrackPtr->
chunkSampleNbTable[mMp4FileDataPtr->videoTrackPtr->currentStsc] +=
1;
#else
mMp4FileDataPtr->videoTrackPtr->
chunkSampleNbTable[mMp4FileDataPtr->videoTrackPtr->currentChunk] +=
1;
#endif
if (auPtr->attribute == AU_RAP)
{
#ifdef _M4MP4W_OPTIMIZE_FOR_PHONE
if (4 *(mMp4FileDataPtr->videoTrackPtr->stssTableEntryNb + 3)
>= mMp4FileDataPtr->videoTrackPtr->nbOfAllocatedStssBlocks
*M4MP4W_STSS_ALLOC_SIZE)
{
M4OSA_TRACE1_0("M4MP4W_processAU : video stss table is full");
return M4WAR_MP4W_OVERSIZE;
}
#else
if (4 *mMp4FileDataPtr->videoTrackPtr->stssTableEntryNb
>= mMp4FileDataPtr->videoTrackPtr->nbOfAllocatedStssBlocks
*M4MP4W_STSS_ALLOC_SIZE)
{
mMp4FileDataPtr->videoTrackPtr->nbOfAllocatedStssBlocks += 1;
mMp4FileDataPtr->videoTrackPtr->TABLE_STSS =
(M4OSA_UInt32 *)M4MP4W_realloc(
(M4OSA_MemAddr32)mMp4FileDataPtr->videoTrackPtr->
TABLE_STSS, ( mMp4FileDataPtr->videoTrackPtr->
nbOfAllocatedStssBlocks
- 1) * M4MP4W_STSS_ALLOC_SIZE,
mMp4FileDataPtr->videoTrackPtr->
nbOfAllocatedStssBlocks
* M4MP4W_STSS_ALLOC_SIZE);
ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->TABLE_STSS
!= M4OSA_NULL, M4ERR_ALLOC);
}
#endif /*_M4MP4W_OPTIMIZE_FOR_PHONE*/
mMp4FileDataPtr->videoTrackPtr->
TABLE_STSS[mMp4FileDataPtr->videoTrackPtr->stssTableEntryNb] =
mMp4FileDataPtr->videoTrackPtr->CommonData.sampleNb;
mMp4FileDataPtr->videoTrackPtr->stssTableEntryNb += 1;
mMp4FileDataPtr->filesize += 4;
}
/* Warning: time conversion cast 64to32! */
mMp4FileDataPtr->videoTrackPtr->CommonData.lastCTS =
(M4MP4W_Time32)auPtr->CTS;
}
else
return M4ERR_BAD_STREAM_ID;
/* I moved some state modification to after we know the sample has been written correctly. */
if ((mMp4FileDataPtr->estimateAudioSize == M4OSA_TRUE)
&& (streamID == VideoStreamID))
{
mMp4FileDataPtr->audioMsStopTime =
(M4MP4W_Time32)(auPtr->CTS * scale_video);
}
if ((mMp4FileDataPtr->estimateAudioSize == M4OSA_FALSE)
|| (streamID == VideoStreamID))
{
/*update fileSize*/
mMp4FileDataPtr->filesize += auPtr->size;
}
if ((mMp4FileDataPtr->estimateAudioSize == M4OSA_TRUE)
&& (streamID == VideoStreamID))
{
/*update filesize with estimated audio data that will be added later. */
/*Warning: Assumption is made that: */
/* - audio samples have constant size (e.g. no sid). */
/* - max audio sample size has been set, and is the actual sample size. */
ERR_CHECK(mMp4FileDataPtr->audioMsChunkDur != 0,
M4WAR_MP4W_NOT_EVALUABLE);
mMp4FileDataPtr->filesize -=
(M4OSA_UInt32)(( mMp4FileDataPtr->videoTrackPtr->CommonData.lastCTS
* scale_video) * (0.05/*always 50 AMR samples per second*/
*(M4OSA_Double)mMp4FileDataPtr->audioTrackPtr->MaxAUSize
+ 16/*additional data for a new chunk*/
/ (M4OSA_Double)mMp4FileDataPtr->audioMsChunkDur));
mMp4FileDataPtr->filesize += (M4OSA_UInt32)(( auPtr->CTS * scale_video)
* (0.05/*always 50 AMR samples per second*/
*(M4OSA_Double)mMp4FileDataPtr->audioTrackPtr->MaxAUSize
+ 16/*additional data for a new chunk*/
/ (M4OSA_Double)mMp4FileDataPtr->audioMsChunkDur));
}
M4OSA_TRACE1_4("processAU : size 0x%x mode %d filesize %lu limit %lu",
auPtr->size, auPtr->attribute, mMp4FileDataPtr->filesize,
mMp4FileDataPtr->MaxFileSize);
return err;
}
/*******************************************************************************/
M4OSA_ERR M4MP4W_closeWrite( M4OSA_Context context )
/*******************************************************************************/
{
M4OSA_ERR err = M4NO_ERROR;
M4OSA_ERR err2 = M4NO_ERROR, err3 = M4NO_ERROR;
/*Warning: test should be done here to ensure context->pContext is not M4OSA_NULL,
but C is not C++...*/
M4MP4W_Mp4FileData *mMp4FileDataPtr = (M4MP4W_Mp4FileData *)context;
M4OSA_UChar camcoder_maj, camcoder_min, camcoder_rev; /*camcoder version*/
M4OSA_Bool bAudio =
(( mMp4FileDataPtr->hasAudio)
&& (mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb
!= 0)); /*((mMp4FileDataPtr->audioTrackPtr != M4OSA_NULL) &&
(mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb != 0));*/
M4OSA_Bool bVideo =
(( mMp4FileDataPtr->hasVideo)
&& (mMp4FileDataPtr->videoTrackPtr->CommonData.sampleNb
!= 0)); /*((mMp4FileDataPtr->videoTrackPtr != M4OSA_NULL) &&
(mMp4FileDataPtr->videoTrackPtr->CommonData.sampleNb != 0));*/
M4OSA_Bool bH263 = M4OSA_FALSE;
M4OSA_Bool bH264 = M4OSA_FALSE;
M4OSA_Bool bMP4V = M4OSA_FALSE;
M4OSA_Bool bAAC = M4OSA_FALSE;
M4OSA_Bool bEVRC = M4OSA_FALSE;
/*intermediate variables*/
M4OSA_UInt32 A, B, N, AB4N;
/*Trak variables*/
M4OSA_UInt32 a_trakId = AudioStreamID; /* (audio=1)*/
/* first trak offset is 32+moovSize, second equals 32+moovSize+1st_track_size*/
M4OSA_UInt32 a_trakOffset = 32;
M4OSA_UInt32 a_sttsSize = 24; /* A (audio=24)*/
M4OSA_UInt32 a_stszSize = 20; /* B (audio=20)*/
M4OSA_UInt32 a_trakSize = 402; /* (audio=402)*/
M4OSA_UInt32 a_mdiaSize = 302; /* (audio=302)*/
M4OSA_UInt32 a_minfSize = 229; /* (audio=229)*/
M4OSA_UInt32 a_stblSize = 169; /* (audio=169)*/
M4OSA_UInt32 a_stsdSize = 69; /* (audio=69 )*/
M4OSA_UInt32 a_esdSize = 53; /* (audio=53 )*/
M4OSA_UInt32 a_dataSize = 0; /* temp: At the end, = currentPos*/
M4MP4W_Time32 a_trakDuration = 0; /* equals lastCTS*/
M4MP4W_Time32 a_msTrakDuration = 0;
M4OSA_UInt32 a_stscSize = 28; /* 16+12*nbchunksaudio*/
M4OSA_UInt32 a_stcoSize = 20; /* 16+4*nbchunksaudio*/
M4OSA_UInt32 v_trakId = VideoStreamID; /* (video=2)*/
/* first trak offset is 32+moovSize, second equals 32+moovSize+1st_track_size*/
M4OSA_UInt32 v_trakOffset = 32;
M4OSA_UInt32 v_sttsSize = 0; /* A (video=16+8J)*/
M4OSA_UInt32 v_stszSize = 0; /* B (video=20+4K)*/
M4OSA_UInt32 v_trakSize = 0; /* (h263=A+B+4N+426), (mp4v=A+B+dsi+4N+448) */
M4OSA_UInt32 v_mdiaSize = 0; /* (h263=A+B+4N+326), (mp4v=A+B+dsi+4N+348) */
M4OSA_UInt32 v_minfSize = 0; /* (h263=A+B+4N+253), (mp4v=A+B+dsi+4N+275) */
M4OSA_UInt32 v_stblSize = 0; /* (h263=A+B+4N+189), (mp4v=A+B+dsi+4N+211) */
M4OSA_UInt32 v_stsdSize = 0; /* (h263=117) , (mp4v=139+dsi )*/
M4OSA_UInt32 v_esdSize = 0; /* (h263=101) , (mp4v=153+dsi )*/
M4OSA_UInt32 v_dataSize = 0; /* temp: At the end, = currentPos*/
M4MP4W_Time32 v_trakDuration = 0; /* equals lastCTS*/
M4MP4W_Time32 v_msTrakDuration = 0;
M4OSA_UInt32 v_stscSize = 28; /* 16+12*nbchunksvideo*/
M4OSA_UInt32 v_stcoSize = 20; /* 16+4*nbchunksvideo*/
/*video variables*/
M4OSA_UInt32 v_stssSize = 0; /* 4*N+16 STSS*/
/*aac & mp4v temp variable*/
M4OSA_UInt8 dsi = 0;
/*H264 variables*/
M4OSA_UInt32 v_avcCSize = 0; /* dsi+15*/
/*MP4V variables*/
M4OSA_UInt32 v_esdsSize = 0; /* dsi+37*/
M4OSA_UInt8 v_ESDescriptorSize =
0; /* dsi+23 (warning: check dsi<105 for coding size on 1 byte)*/
M4OSA_UInt8 v_DCDescriptorSize = 0; /* dsi+15*/
/*AAC variables*/
M4OSA_UInt32 a_esdsSize = 0; /* dsi+37*/
M4OSA_UInt8 a_ESDescriptorSize =
0; /* dsi+23 (warning: check dsi<105 for coding size on 1 byte)*/
M4OSA_UInt8 a_DCDescriptorSize = 0; /* dsi+15*/
/*General variables*/
/* audio chunk size + video chunk size*/
M4OSA_UInt32 mdatSize = 8;
M4OSA_UInt32 moovSize = 116; /* 116 + 402(audio) + (A+B+4N+426)(h263) or */
/* (A+B+dsi+4N+448)(mp4v) */
M4OSA_UInt32 creationTime; /* C */
/*flag to set up the chunk interleave strategy*/
M4OSA_Bool bInterleaveAV =
(bAudio && bVideo && (mMp4FileDataPtr->InterleaveDur != 0));
M4OSA_Context fileWriterContext = mMp4FileDataPtr->fileWriterContext;
M4OSA_UInt32 i;
M4OSA_Double scale_audio = 0.0;
M4OSA_Double scale_video = 0.0;
M4MP4W_Time32 delta;
#ifndef _M4MP4W_MOOV_FIRST
M4OSA_FilePosition moovPos, mdatPos;
#endif /*_M4MP4W_MOOV_FIRST*/
ERR_CHECK(context != M4OSA_NULL, M4ERR_PARAMETER);
/*macro state */
mMp4FileDataPtr->state = M4MP4W_closed;
/*if no data !*/
if ((!bAudio) && (!bVideo))
{
err = M4NO_ERROR; /*would be better to return a warning ?*/
goto cleanup;
}
#ifdef _M4MP4W_RESERVED_MOOV_DISK_SPACE
/* Remove safety file to make room for what needs to be written out here
(chunk flushing and moov). */
if (M4OSA_TRUE == mMp4FileDataPtr->cleanSafetyFile)
{
M4OSA_Context tempContext;
err = mMp4FileDataPtr->fileWriterFunctions->openWrite(&tempContext,
mMp4FileDataPtr->safetyFileUrl,
M4OSA_kFileWrite | M4OSA_kFileCreate);
if (M4NO_ERROR != err)
goto cleanup;
err = mMp4FileDataPtr->fileWriterFunctions->closeWrite(tempContext);
if (M4NO_ERROR != err)
goto cleanup;
mMp4FileDataPtr->safetyFileUrl = M4OSA_NULL;
mMp4FileDataPtr->cleanSafetyFile = M4OSA_FALSE;
}
#endif /* _M4MP4W_RESERVED_MOOV_DISK_SPACE */
if (bVideo)
{
if ((M4OSA_NULL == mMp4FileDataPtr->videoTrackPtr->chunkOffsetTable)
|| (M4OSA_NULL == mMp4FileDataPtr->videoTrackPtr->chunkSizeTable)
|| (M4OSA_NULL
== mMp4FileDataPtr->videoTrackPtr->chunkSampleNbTable)
|| (M4OSA_NULL
== mMp4FileDataPtr->videoTrackPtr->chunkTimeMsTable)
|| (M4OSA_NULL == mMp4FileDataPtr->videoTrackPtr->TABLE_STSZ)
|| (M4OSA_NULL == mMp4FileDataPtr->videoTrackPtr->TABLE_STTS)
|| (M4OSA_NULL == mMp4FileDataPtr->videoTrackPtr->TABLE_STSS))
{
mMp4FileDataPtr->fileWriterFunctions->closeWrite(
fileWriterContext); /**< close the stream anyway */
M4MP4W_freeContext(context); /**< Free the context content */
return M4ERR_ALLOC;
}
/*video microstate*/
mMp4FileDataPtr->videoTrackPtr->microState = M4MP4W_closed;
/*current chunk is the last one and gives the total number of video chunks (-1)*/
for ( i = 0; i < mMp4FileDataPtr->videoTrackPtr->currentChunk; i++ )
{
v_dataSize += mMp4FileDataPtr->videoTrackPtr->chunkSizeTable[i];
}
#ifndef _M4MP4W_MOOV_FIRST
#ifndef _M4MP4W_UNBUFFERED_VIDEO
/*flush chunk*/
if (mMp4FileDataPtr->videoTrackPtr->currentPos > 0)
{
err = M4MP4W_putBlock(mMp4FileDataPtr->videoTrackPtr->Chunk[0],
mMp4FileDataPtr->videoTrackPtr->currentPos,
mMp4FileDataPtr->fileWriterFunctions,
mMp4FileDataPtr->fileWriterContext);
if (M4NO_ERROR != err)
goto cleanup;
}
#endif
M4OSA_TRACE1_0("flush video | CLOSE");
M4OSA_TRACE1_3("current chunk = %d offset = 0x%x size = 0x%08X",
mMp4FileDataPtr->videoTrackPtr->currentChunk,
mMp4FileDataPtr->absoluteCurrentPos,
mMp4FileDataPtr->videoTrackPtr->currentPos);
/*update chunk offset*/
mMp4FileDataPtr->videoTrackPtr->
chunkOffsetTable[mMp4FileDataPtr->videoTrackPtr->currentChunk] =
mMp4FileDataPtr->absoluteCurrentPos;
/*add chunk size to absoluteCurrentPos*/
mMp4FileDataPtr->absoluteCurrentPos +=
mMp4FileDataPtr->videoTrackPtr->currentPos;
#endif /*_M4MP4W_MOOV_FIRST*/
/*update last chunk size, and add this value to v_dataSize*/
mMp4FileDataPtr->videoTrackPtr->
chunkSizeTable[mMp4FileDataPtr->videoTrackPtr->currentChunk] =
mMp4FileDataPtr->videoTrackPtr->currentPos;
v_dataSize +=
mMp4FileDataPtr->videoTrackPtr->currentPos; /*add last chunk size*/
v_trakDuration = mMp4FileDataPtr->videoTrackPtr->
CommonData.lastCTS; /* equals lastCTS*/
/* bugfix: if a new chunk was just created, cancel it before to close */
if ((mMp4FileDataPtr->videoTrackPtr->currentChunk != 0)
&& (mMp4FileDataPtr->videoTrackPtr->currentPos == 0))
{
mMp4FileDataPtr->videoTrackPtr->currentChunk--;
}
#ifdef _M4MP4W_UNBUFFERED_VIDEO
if ((mMp4FileDataPtr->videoTrackPtr->
chunkSampleNbTable[mMp4FileDataPtr->videoTrackPtr->
currentStsc] & 0xFFF) == 0)
{