| /* |
| * 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) |
| { |
| mMp4FileDataPtr->videoTrackPtr->currentStsc--; |
| } |
| |
| #endif /*_M4MP4W_UNBUFFERED_VIDEO*/ |
| |
| /* Last sample duration */ |
| /* If we have the file duration we use it, else we duplicate the last AU */ |
| |
| if (mMp4FileDataPtr->MaxFileDuration > 0) |
| { |
| /* use max file duration to calculate delta of last AU */ |
| delta = mMp4FileDataPtr->MaxFileDuration |
| - mMp4FileDataPtr->videoTrackPtr->CommonData.lastCTS; |
| v_trakDuration = mMp4FileDataPtr->MaxFileDuration; |
| |
| if (mMp4FileDataPtr->videoTrackPtr->CommonData.sampleNb > 1) |
| { |
| /* if more than 1 frame, create a new stts entry (else already created) */ |
| mMp4FileDataPtr->videoTrackPtr->CommonData.sttsTableEntryNb++; |
| } |
| |
| #ifdef _M4MP4W_OPTIMIZE_FOR_PHONE |
| |
| M4MP4W_put32_Lo(&mMp4FileDataPtr->videoTrackPtr-> |
| TABLE_STTS[mMp4FileDataPtr->videoTrackPtr-> |
| CommonData.sttsTableEntryNb - 1], 1); |
| M4MP4W_put32_Hi(&mMp4FileDataPtr->videoTrackPtr-> |
| TABLE_STTS[mMp4FileDataPtr->videoTrackPtr-> |
| CommonData.sttsTableEntryNb - 1], delta); |
| |
| #else |
| |
| mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[2 |
| *(mMp4FileDataPtr->videoTrackPtr->CommonData.sttsTableEntryNb |
| - 1)] = 1; |
| mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[2 |
| *(mMp4FileDataPtr->videoTrackPtr->CommonData.sttsTableEntryNb |
| - 1) + 1] = delta; |
| |
| #endif |
| |
| } |
| else |
| { |
| /* duplicate the delta of the previous frame */ |
| if (mMp4FileDataPtr->videoTrackPtr->CommonData.sampleNb > 1) |
| { |
| /* if more than 1 frame, duplicate the stts entry (else already exists) */ |
| #ifdef _M4MP4W_OPTIMIZE_FOR_PHONE |
| |
| v_trakDuration += |
| M4MP4W_get32_Hi(&mMp4FileDataPtr->videoTrackPtr-> |
| TABLE_STTS[mMp4FileDataPtr->videoTrackPtr-> |
| CommonData.sttsTableEntryNb - 1]); |
| mMp4FileDataPtr->videoTrackPtr-> |
| TABLE_STTS[mMp4FileDataPtr->videoTrackPtr-> |
| CommonData.sttsTableEntryNb - 1] += 1; |
| |
| #else |
| |
| v_trakDuration += mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[2 |
| * (mMp4FileDataPtr->videoTrackPtr-> |
| CommonData.sttsTableEntryNb - 1) + 1]; |
| mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[2 *( |
| mMp4FileDataPtr->videoTrackPtr-> |
| CommonData.sttsTableEntryNb - 1)] += 1; |
| |
| #endif |
| |
| } |
| else |
| { |
| M4OSA_TRACE1_0("M4MP4W_closeWrite : ! videoTrackPtr,\ |
| cannot know the duration of the unique AU !"); |
| /* If there is an audio track, we use it as a file duration |
| (and so, as AU duration...) */ |
| if (mMp4FileDataPtr->audioTrackPtr != M4OSA_NULL) |
| { |
| M4OSA_TRACE1_0( |
| "M4MP4W_closeWrite : ! Let's use the audio track duration !"); |
| mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[1] = |
| (M4OSA_UInt32)( |
| mMp4FileDataPtr->audioTrackPtr->CommonData.lastCTS |
| * (1000.0 / mMp4FileDataPtr->audioTrackPtr-> |
| CommonData.timescale)); |
| v_trakDuration = |
| mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[1]; |
| } |
| /* Else, we use a MAGICAL value (66 ms) */ |
| else |
| { |
| M4OSA_TRACE1_0( |
| "M4MP4W_closeWrite : ! No audio track -> use magical value (66) !"); /* */ |
| mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[1] = 66; |
| v_trakDuration = 66; |
| } |
| } |
| } |
| |
| /* Calculate table sizes */ |
| A = v_sttsSize = 16 + 8 * mMp4FileDataPtr->videoTrackPtr-> |
| CommonData.sttsTableEntryNb; /* A (video=16+8J)*/ |
| B = v_stszSize = 20 + 4 * mMp4FileDataPtr->videoTrackPtr-> |
| CommonData.sampleNb; /* B (video=20+4K)*/ |
| N = mMp4FileDataPtr->videoTrackPtr->stssTableEntryNb; |
| AB4N = A + B + 4 * N; |
| |
| scale_video = |
| 1000.0 / mMp4FileDataPtr->videoTrackPtr->CommonData.timescale; |
| v_msTrakDuration = (M4OSA_UInt32)(v_trakDuration * scale_video); |
| |
| /*Convert integers in the table from LE into BE*/ |
| #ifndef _M4MP4W_OPTIMIZE_FOR_PHONE |
| |
| M4MP4W_table32ToBE(mMp4FileDataPtr->videoTrackPtr->TABLE_STSZ, |
| mMp4FileDataPtr->videoTrackPtr->CommonData.sampleNb); |
| M4MP4W_table32ToBE(mMp4FileDataPtr->videoTrackPtr->TABLE_STTS, |
| 2 * (mMp4FileDataPtr->videoTrackPtr->CommonData.sttsTableEntryNb)); |
| |
| #endif |
| |
| M4MP4W_table32ToBE(mMp4FileDataPtr->videoTrackPtr->TABLE_STSS, |
| mMp4FileDataPtr->videoTrackPtr->stssTableEntryNb); |
| |
| if (mMp4FileDataPtr->videoTrackPtr->CommonData.trackType |
| == M4SYS_kH263) |
| { |
| bH263 = M4OSA_TRUE; |
| v_trakSize = AB4N + 426; /* (h263=A+B+4N+426)*/ |
| v_mdiaSize = AB4N + 326; /* (h263=A+B+4N+326)*/ |
| v_minfSize = AB4N + 253; /* (h263=A+B+4N+253)*/ |
| v_stblSize = AB4N + 189; /* (h263=A+B+4N+189)*/ |
| v_stsdSize = 117; /* (h263=117)*/ |
| v_esdSize = 101; /* (h263=101)*/ |
| |
| moovSize += AB4N + 426; |
| |
| if (((M4OSA_Int32)mMp4FileDataPtr->videoTrackPtr->avgBitrate) != -1) |
| { |
| /*the optional 'bitr' atom is appended to the dsi,so filesize is 16 bytes bigger*/ |
| v_trakSize += 16; |
| v_mdiaSize += 16; |
| v_minfSize += 16; |
| v_stblSize += 16; |
| v_stsdSize += 16; |
| v_esdSize += 16; |
| moovSize += 16; |
| } |
| } |
| else if (mMp4FileDataPtr->videoTrackPtr->CommonData.trackType |
| == M4SYS_kH264) |
| { |
| bH264 = M4OSA_TRUE; |
| /* For H264 there is no default DSI, and its presence is mandatory, |
| so check the DSI has been set*/ |
| if (0 == mMp4FileDataPtr->videoTrackPtr->dsiSize |
| || M4OSA_NULL == mMp4FileDataPtr->videoTrackPtr->DSI) |
| { |
| M4OSA_TRACE1_0( |
| "M4MP4W_closeWrite: error, no H264 DSI has been set!"); |
| err = M4ERR_STATE; |
| goto cleanup; |
| } |
| |
| /*H264 sizes of the atom*/ |
| |
| // Remove the hardcoded DSI values of H264Block2 |
| // TODO: check bMULPPSSPS case |
| v_avcCSize = sizeof(M4OSA_UInt32) + sizeof(H264Block2) + |
| mMp4FileDataPtr->videoTrackPtr->dsiSize; |
| |
| v_trakSize = AB4N + v_avcCSize + 411; |
| v_mdiaSize = AB4N + v_avcCSize + 311; |
| v_minfSize = AB4N + v_avcCSize + 238; |
| v_stblSize = AB4N + v_avcCSize + 174; |
| v_stsdSize = v_avcCSize + 102; |
| v_esdSize = v_avcCSize + 86; |
| |
| moovSize += AB4N + v_avcCSize + 411; |
| |
| } |
| else if (mMp4FileDataPtr->videoTrackPtr->CommonData.trackType |
| == M4SYS_kMPEG_4) |
| { |
| bMP4V = M4OSA_TRUE; |
| /* For MPEG4 there is no default DSI, and its presence is mandatory, |
| so check the DSI has been set*/ |
| if (0 == mMp4FileDataPtr->videoTrackPtr->dsiSize |
| || M4OSA_NULL == mMp4FileDataPtr->videoTrackPtr->DSI) |
| { |
| M4OSA_TRACE1_0( |
| "M4MP4W_closeWrite: error, no MPEG4 DSI has been set!"); |
| err = M4ERR_STATE; |
| goto cleanup; |
| } |
| |
| /*MP4V variables*/ |
| dsi = mMp4FileDataPtr->videoTrackPtr->dsiSize; |
| v_esdsSize = 37 + dsi; /* dsi+37*/ |
| v_ESDescriptorSize = |
| 23 |
| + dsi; /* dsi+23 (warning: check dsi<105 for coding size on 1 byte)*/ |
| v_DCDescriptorSize = 15 + dsi; /* dsi+15*/ |
| |
| v_trakSize = AB4N + dsi + 448; /* (mp4v=A+B+dsi+4N+448) */ |
| v_mdiaSize = AB4N + dsi + 348; /* (mp4v=A+B+dsi+4N+348) */ |
| v_minfSize = AB4N + dsi + 275; /* (mp4v=A+B+dsi+4N+275) */ |
| v_stblSize = AB4N + dsi + 211; /* (mp4v=A+B+dsi+4N+211) */ |
| v_stsdSize = dsi + 139; /* (mp4v=139+dsi)*/ |
| v_esdSize = dsi + 123; /* (mp4v=123+dsi)*/ |
| |
| moovSize += AB4N + dsi + 448; |
| } |
| |
| /*video variables*/ |
| v_stssSize = 16 + 4 * N; /* 4*N+16 STSS*/ |
| |
| #ifdef _M4MP4W_OPTIMIZE_FOR_PHONE |
| /* stsc update */ |
| |
| v_stscSize += 12 * mMp4FileDataPtr->videoTrackPtr->currentStsc; |
| v_stblSize += 12 * mMp4FileDataPtr->videoTrackPtr->currentStsc; |
| v_minfSize += 12 * mMp4FileDataPtr->videoTrackPtr->currentStsc; |
| v_mdiaSize += 12 * mMp4FileDataPtr->videoTrackPtr->currentStsc; |
| v_trakSize += 12 * mMp4FileDataPtr->videoTrackPtr->currentStsc; |
| moovSize += 12 * mMp4FileDataPtr->videoTrackPtr->currentStsc; |
| |
| /* stco update */ |
| v_stcoSize += 4 * mMp4FileDataPtr->videoTrackPtr->currentChunk; |
| v_stblSize += 4 * mMp4FileDataPtr->videoTrackPtr->currentChunk; |
| v_minfSize += 4 * mMp4FileDataPtr->videoTrackPtr->currentChunk; |
| v_mdiaSize += 4 * mMp4FileDataPtr->videoTrackPtr->currentChunk; |
| v_trakSize += 4 * mMp4FileDataPtr->videoTrackPtr->currentChunk; |
| moovSize += 4 * mMp4FileDataPtr->videoTrackPtr->currentChunk; |
| |
| #else |
| /*stsc/stco update*/ |
| |
| v_stscSize += 12 * mMp4FileDataPtr->videoTrackPtr->currentChunk; |
| v_stcoSize += 4 * mMp4FileDataPtr->videoTrackPtr->currentChunk; |
| v_stblSize += 16 * mMp4FileDataPtr->videoTrackPtr->currentChunk; |
| v_minfSize += 16 * mMp4FileDataPtr->videoTrackPtr->currentChunk; |
| v_mdiaSize += 16 * mMp4FileDataPtr->videoTrackPtr->currentChunk; |
| v_trakSize += 16 * mMp4FileDataPtr->videoTrackPtr->currentChunk; |
| moovSize += 16 * mMp4FileDataPtr->videoTrackPtr->currentChunk; |
| |
| #endif |
| |
| /*update last chunk time*/ |
| |
| mMp4FileDataPtr->videoTrackPtr-> |
| chunkTimeMsTable[mMp4FileDataPtr->videoTrackPtr->currentChunk] = |
| v_msTrakDuration; |
| } |
| |
| if (bAudio) |
| { |
| if ((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)) |
| { |
| mMp4FileDataPtr->fileWriterFunctions->closeWrite( |
| fileWriterContext); /**< close the stream anyway */ |
| M4MP4W_freeContext(context); /**< Free the context content */ |
| return M4ERR_ALLOC; |
| } |
| |
| /*audio microstate*/ |
| mMp4FileDataPtr->audioTrackPtr->microState = M4MP4W_closed; |
| |
| if (mMp4FileDataPtr->audioTrackPtr->CommonData.trackType == M4SYS_kAAC) |
| { |
| bAAC = |
| M4OSA_TRUE; /*else, audio is implicitely amr in the following*/ |
| dsi = mMp4FileDataPtr->audioTrackPtr->dsiSize; /*variable size*/ |
| |
| a_esdsSize = 37 + dsi; /* dsi+37*/ |
| a_ESDescriptorSize = |
| 23 |
| + dsi; /* dsi+23 (warning: check dsi<105 for coding size on 1 byte)*/ |
| a_DCDescriptorSize = 15 + dsi; /* dsi+15*/ |
| |
| a_esdSize = dsi + 73; /*overwrite a_esdSize with aac value*/ |
| /*add dif. between amr & aac sizes: (- 53 + dsi + 37)*/ |
| a_stsdSize += dsi + 20; |
| a_stblSize += dsi + 20; |
| a_minfSize += dsi + 20; |
| a_mdiaSize += dsi + 20; |
| a_trakSize += dsi + 20; |
| moovSize += dsi + 20; |
| } |
| |
| if (mMp4FileDataPtr->audioTrackPtr->CommonData.trackType |
| == M4SYS_kEVRC) |
| { |
| bEVRC = |
| M4OSA_TRUE; /*else, audio is implicitely amr in the following*/ |
| |
| /* evrc dsi is only 6 bytes while amr dsi is 9 bytes,all other blocks are unchanged */ |
| a_esdSize -= 3; |
| a_stsdSize -= 3; |
| a_stblSize -= 3; |
| a_minfSize -= 3; |
| a_mdiaSize -= 3; |
| a_trakSize -= 3; |
| moovSize -= 3; |
| } |
| |
| if (mMp4FileDataPtr->audioTrackPtr->CommonData.sampleSize == 0) |
| { |
| if (M4OSA_NULL == mMp4FileDataPtr->audioTrackPtr->TABLE_STSZ) |
| { |
| mMp4FileDataPtr->fileWriterFunctions->closeWrite( |
| fileWriterContext); /**< close the stream anyway */ |
| M4MP4W_freeContext(context); /**< Free the context content */ |
| return M4ERR_ALLOC; |
| } |
| /*Convert integers in the table from LE into BE*/ |
| M4MP4W_table32ToBE(mMp4FileDataPtr->audioTrackPtr->TABLE_STSZ, |
| mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb); |
| a_stszSize += |
| 4 * mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb; |
| a_stblSize += |
| 4 * mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb; |
| a_minfSize += |
| 4 * mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb; |
| a_mdiaSize += |
| 4 * mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb; |
| a_trakSize += |
| 4 * mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb; |
| moovSize += 4 * mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb; |
| } |
| |
| moovSize += 402; |
| |
| /*current chunk is the last one and gives the total number of audio chunks (-1)*/ |
| for ( i = 0; i < mMp4FileDataPtr->audioTrackPtr->currentChunk; i++ ) |
| { |
| a_dataSize += mMp4FileDataPtr->audioTrackPtr->chunkSizeTable[i]; |
| } |
| |
| #ifndef _M4MP4W_MOOV_FIRST |
| /*flush chunk*/ |
| |
| if (mMp4FileDataPtr->audioTrackPtr->currentPos > 0) |
| { |
| err = M4MP4W_putBlock(mMp4FileDataPtr->audioTrackPtr->Chunk[0], |
| mMp4FileDataPtr->audioTrackPtr->currentPos, |
| mMp4FileDataPtr->fileWriterFunctions, |
| mMp4FileDataPtr->fileWriterContext); |
| |
| if (M4NO_ERROR != err) |
| goto cleanup; |
| } |
| |
| M4OSA_TRACE1_0("flush audio | CLOSE"); |
| M4OSA_TRACE1_2("current chunk = %d offset = 0x%x", |
| mMp4FileDataPtr->audioTrackPtr->currentChunk, |
| mMp4FileDataPtr->absoluteCurrentPos); |
| |
| /*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 last chunk size, and add this value to a_dataSize*/ |
| |
| mMp4FileDataPtr->audioTrackPtr-> |
| chunkSizeTable[mMp4FileDataPtr->audioTrackPtr->currentChunk] = |
| mMp4FileDataPtr->audioTrackPtr->currentPos; |
| a_dataSize += |
| mMp4FileDataPtr->audioTrackPtr->currentPos; /*add last chunk size*/ |
| |
| /* 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--; |
| } |
| #ifdef _M4MP4W_UNBUFFERED_VIDEO |
| |
| if ((mMp4FileDataPtr->audioTrackPtr-> |
| chunkSampleNbTable[mMp4FileDataPtr->audioTrackPtr-> |
| currentStsc] & 0xFFF) == 0) |
| { |
| mMp4FileDataPtr->audioTrackPtr->currentStsc--; |
| } |
| |
| #endif /*_M4MP4W_UNBUFFERED_VIDEO*/ |
| |
| a_trakDuration = mMp4FileDataPtr->audioTrackPtr-> |
| CommonData.lastCTS; /* equals lastCTS*/ |
| /* add last sample dur */ |
| |
| if (mMp4FileDataPtr->audioTrackPtr->CommonData.sttsTableEntryNb != 1) |
| { |
| #ifdef DUPLICATE_STTS_IN_LAST_AU |
| /*increase of 1 the number of consecutive AUs with same duration*/ |
| |
| mMp4FileDataPtr->audioTrackPtr->TABLE_STTS[2 |
| *(mMp4FileDataPtr->audioTrackPtr->CommonData.sttsTableEntryNb |
| - 1) - 2] += 1; |
| |
| #endif /*DUPLICATE_STTS_IN_LAST_AU*/ |
| |
| a_trakDuration += mMp4FileDataPtr->audioTrackPtr->TABLE_STTS[2 |
| * (mMp4FileDataPtr->audioTrackPtr-> |
| CommonData.sttsTableEntryNb - 1) - 1]; |
| } |
| else if (0 == mMp4FileDataPtr->audioTrackPtr->CommonData.lastCTS) |
| { |
| if (mMp4FileDataPtr->audioTrackPtr->CommonData.trackType |
| == M4SYS_kAMR) |
| { |
| if (12200 == mMp4FileDataPtr->audioTrackPtr->avgBitrate) |
| { |
| a_trakDuration = a_dataSize / 32 |
| * mMp4FileDataPtr->audioTrackPtr->sampleDuration; |
| } |
| else if (10200 == mMp4FileDataPtr->audioTrackPtr->avgBitrate) |
| { |
| a_trakDuration = a_dataSize / 27 |
| * mMp4FileDataPtr->audioTrackPtr->sampleDuration; |
| } |
| else if (7950 == mMp4FileDataPtr->audioTrackPtr->avgBitrate) |
| { |
| a_trakDuration = a_dataSize / 21 |
| * mMp4FileDataPtr->audioTrackPtr->sampleDuration; |
| } |
| else if (7400 == mMp4FileDataPtr->audioTrackPtr->avgBitrate) |
| { |
| a_trakDuration = a_dataSize / 20 |
| * mMp4FileDataPtr->audioTrackPtr->sampleDuration; |
| } |
| else if (6700 == mMp4FileDataPtr->audioTrackPtr->avgBitrate) |
| { |
| a_trakDuration = a_dataSize / 18 |
| * mMp4FileDataPtr->audioTrackPtr->sampleDuration; |
| } |
| else if (5900 == mMp4FileDataPtr->audioTrackPtr->avgBitrate) |
| { |
| a_trakDuration = a_dataSize / 16 |
| * mMp4FileDataPtr->audioTrackPtr->sampleDuration; |
| } |
| else if (5150 == mMp4FileDataPtr->audioTrackPtr->avgBitrate) |
| { |
| a_trakDuration = a_dataSize / 14 |
| * mMp4FileDataPtr->audioTrackPtr->sampleDuration; |
| } |
| else if (4750 == mMp4FileDataPtr->audioTrackPtr->avgBitrate) |
| { |
| a_trakDuration = a_dataSize / 13 |
| * mMp4FileDataPtr->audioTrackPtr->sampleDuration; |
| } |
| } |
| } |
| |
| scale_audio = |
| 1000.0 / mMp4FileDataPtr->audioTrackPtr->CommonData.timescale; |
| a_msTrakDuration = (M4OSA_UInt32)(a_trakDuration * scale_audio); |
| |
| #ifdef _M4MP4W_OPTIMIZE_FOR_PHONE |
| /* stsc update */ |
| |
| a_stscSize += 12 * mMp4FileDataPtr->audioTrackPtr->currentStsc; |
| a_stblSize += 12 * mMp4FileDataPtr->audioTrackPtr->currentStsc; |
| a_minfSize += 12 * mMp4FileDataPtr->audioTrackPtr->currentStsc; |
| a_mdiaSize += 12 * mMp4FileDataPtr->audioTrackPtr->currentStsc; |
| a_trakSize += 12 * mMp4FileDataPtr->audioTrackPtr->currentStsc; |
| moovSize += 12 * mMp4FileDataPtr->audioTrackPtr->currentStsc; |
| |
| /* stso update */ |
| a_stcoSize += 4 * mMp4FileDataPtr->audioTrackPtr->currentChunk; |
| a_stblSize += 4 * mMp4FileDataPtr->audioTrackPtr->currentChunk; |
| a_minfSize += 4 * mMp4FileDataPtr->audioTrackPtr->currentChunk; |
| a_mdiaSize += 4 * mMp4FileDataPtr->audioTrackPtr->currentChunk; |
| a_trakSize += 4 * mMp4FileDataPtr->audioTrackPtr->currentChunk; |
| moovSize += 4 * mMp4FileDataPtr->audioTrackPtr->currentChunk; |
| |
| #else |
| /*stsc/stco update*/ |
| |
| a_stscSize += 12 * mMp4FileDataPtr->audioTrackPtr->currentChunk; |
| a_stcoSize += 4 * mMp4FileDataPtr->audioTrackPtr->currentChunk; |
| a_stblSize += 16 * mMp4FileDataPtr->audioTrackPtr->currentChunk; |
| a_minfSize += 16 * mMp4FileDataPtr->audioTrackPtr->currentChunk; |
| a_mdiaSize += 16 * mMp4FileDataPtr->audioTrackPtr->currentChunk; |
| a_trakSize += 16 * mMp4FileDataPtr->audioTrackPtr->currentChunk; |
| moovSize += 16 * mMp4FileDataPtr->audioTrackPtr->currentChunk; |
| |
| #endif |
| |
| /* compute the new size of stts*/ |
| |
| a_sttsSize = 16 + 8 * (mMp4FileDataPtr->audioTrackPtr-> |
| CommonData.sttsTableEntryNb - 1); |
| |
| moovSize += a_sttsSize - 24; |
| a_mdiaSize += a_sttsSize - 24; |
| a_minfSize += a_sttsSize - 24; |
| a_stblSize += a_sttsSize - 24; |
| a_trakSize += a_sttsSize - 24; |
| |
| /*update last chunk time*/ |
| mMp4FileDataPtr->audioTrackPtr-> |
| chunkTimeMsTable[mMp4FileDataPtr->audioTrackPtr->currentChunk] = |
| a_msTrakDuration; |
| } |
| |
| /* changing the way the mdat size is computed. |
| The real purpose of the mdat size is to know the amount to skip to get to the next |
| atom, which is the moov; the size of media in the mdat is almost secondary. Therefore, |
| it is of utmost importance that the mdat size "points" to where the moov actually |
| begins. Now, the moov begins right after the last data we wrote, so how could the sum |
| of all chunk sizes be different from the total size of what has been written? Well, it |
| can happen when the writing was unexpectedly stopped (because of lack of disk space, |
| for instance), in this case a chunk may be partially written (the partial write is not |
| necessarily erased) but it may not be reflected in the chunk size list (which may |
| believe it hasn't been written or on the contrary that it has been fully written). In |
| the case of such a mismatch, there is either unused data in the mdat (not very good, |
| but tolerable) or when reading the last chunk it will read the beginning of the moov |
| as part of the chunk (which means the last chunk won't be correctly decoded), both of |
| which are still better than losing the whole recording. In the long run it'll probably |
| be attempted to always clean up back to a consistent state, but at any rate it is |
| always safer to have the mdat size be computed using the position where the moov |
| actually begins, rather than using the size it is thought the mdat has. |
| |
| Therefore, I will record where we are just before writing the moov, to serve when |
| updating the mdat size. */ |
| |
| /* mdatSize += a_dataSize + v_dataSize; *//*TODO allow for multiple chunks*/ |
| |
| /* End of Pierre Lebeaupin 19/12/2007: changing the way the mdat size is computed. */ |
| |
| /* first trak offset is 32+moovSize, second equals 32+moovSize+1st_track_size*/ |
| a_trakOffset += moovSize; |
| v_trakOffset += moovSize/*+ a_dataSize*/; |
| |
| if (bInterleaveAV == M4OSA_FALSE) |
| v_trakOffset += a_dataSize; |
| |
| /*system time since 1970 */ |
| #ifndef _M4MP4W_DONT_USE_TIME_H |
| |
| time((time_t *)&creationTime); |
| /*convert into time since 1/1/1904 00h00 (normative)*/ |
| creationTime += 2082841761; /*nb of sec between 1904 and 1970*/ |
| |
| #else /*_M4MP4W_DONT_USE_TIME_H*/ |
| |
| creationTime = |
| 0xBBD09100; /* = 7/11/2003 00h00 ; in hexa because of code scrambler limitation with |
| large integers */ |
| |
| #endif /*_M4MP4W_DONT_USE_TIME_H*/ |
| |
| mMp4FileDataPtr->duration = |
| max(a_msTrakDuration, v_msTrakDuration); /*max audio/video*/ |
| |
| #ifdef _M4MP4W_MOOV_FIRST |
| /*open file in write binary mode*/ |
| |
| err = mMp4FileDataPtr->fileWriterFunctions->openWrite(&fileWriterContext, |
| mMp4FileDataPtr->url, 0x22); |
| ERR_CHECK(err == M4NO_ERROR, err); |
| |
| /*ftyp atom*/ |
| if (mMp4FileDataPtr->ftyp.major_brand != 0) |
| { |
| M4OSA_UInt32 i; |
| |
| /* Put customized ftyp box */ |
| CLEANUPonERR(M4MP4W_putBE32(16 |
| + (mMp4FileDataPtr->ftyp.nbCompatibleBrands * 4), |
| mMp4FileDataPtr->fileWriterFunctions, |
| mMp4FileDataPtr->fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(M4MPAC_FTYP_TAG, |
| mMp4FileDataPtr->fileWriterFunctions, |
| mMp4FileDataPtr->fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(mMp4FileDataPtr->ftyp.major_brand, |
| mMp4FileDataPtr->fileWriterFunctions, |
| mMp4FileDataPtr->fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(mMp4FileDataPtr->ftyp.minor_version, |
| mMp4FileDataPtr->fileWriterFunctions, |
| mMp4FileDataPtr->fileWriterContext)); |
| |
| for ( i = 0; i < mMp4FileDataPtr->ftyp.nbCompatibleBrands; i++ ) |
| { |
| CLEANUPonERR( |
| M4MP4W_putBE32(mMp4FileDataPtr->ftyp.compatible_brands[i], |
| mMp4FileDataPtr->fileWriterFunctions, |
| mMp4FileDataPtr->fileWriterContext)); |
| } |
| } |
| else |
| { |
| /* Put default ftyp box */ |
| CLEANUPonERR(M4MP4W_putBlock(Default_ftyp, sizeof(Default_ftyp), |
| mMp4FileDataPtr->fileWriterFunctions, |
| mMp4FileDataPtr->fileWriterContext)); |
| } |
| |
| #endif /*_M4MP4W_MOOV_FIRST*/ |
| |
| #ifndef _M4MP4W_MOOV_FIRST |
| /* seek is used to get the current position relative to the start of the file. */ |
| /* ... or rather, seek used to be used for that, but it has been found this functionality |
| is not reliably, or sometimes not at all, implemented in the various OSALs, so we now avoid |
| using it. */ |
| /* Notice this new method assumes we're at the end of the file, this will break if ever we |
| are overwriting a larger file. */ |
| |
| CLEANUPonERR(mMp4FileDataPtr->fileWriterFunctions->getOption( |
| mMp4FileDataPtr->fileWriterContext, |
| M4OSA_kFileWriteGetFileSize, (M4OSA_DataOption *) &moovPos)); |
| /* moovPos will be used after writing the moov. */ |
| |
| #endif /*_M4MP4W_MOOV_FIRST*/ |
| |
| CLEANUPonERR(M4MP4W_putBE32(moovSize, mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock3, sizeof(CommonBlock3), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(creationTime, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(creationTime, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock4, sizeof(CommonBlock4), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(mMp4FileDataPtr->duration, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock5, sizeof(CommonBlock5), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| if (bAudio) |
| { |
| CLEANUPonERR(M4MP4W_putBE32(a_trakSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock6, sizeof(CommonBlock6), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(creationTime, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(creationTime, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(a_trakId, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock7, sizeof(CommonBlock7), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(a_msTrakDuration, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock7bis, sizeof(CommonBlock7bis), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(AMRBlock1, sizeof(AMRBlock1), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*audio*/ |
| CLEANUPonERR(M4MP4W_putBE32(a_mdiaSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock8, sizeof(CommonBlock8), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(creationTime, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(creationTime, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR( |
| M4MP4W_putBE32(mMp4FileDataPtr->audioTrackPtr->CommonData.timescale, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(a_trakDuration, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock9, sizeof(CommonBlock9), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(AMRBlock1_1, sizeof(AMRBlock1_1), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*audio*/ |
| CLEANUPonERR(M4MP4W_putBE32(a_minfSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock10, sizeof(CommonBlock10), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(a_stblSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock11, sizeof(CommonBlock11), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(a_sttsSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock12, sizeof(CommonBlock12), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| CLEANUPonERR(M4MP4W_putBE32( |
| mMp4FileDataPtr->audioTrackPtr->CommonData.sttsTableEntryNb - 1, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| /*invert the table data to bigendian*/ |
| M4MP4W_table32ToBE(mMp4FileDataPtr->audioTrackPtr->TABLE_STTS, |
| 2 * (mMp4FileDataPtr->audioTrackPtr->CommonData.sttsTableEntryNb |
| - 1)); |
| CLEANUPonERR(M4MP4W_putBlock((const M4OSA_UChar |
| *)mMp4FileDataPtr->audioTrackPtr->TABLE_STTS, |
| ( mMp4FileDataPtr->audioTrackPtr->CommonData.sttsTableEntryNb - 1) |
| * 8, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*audio*/ |
| |
| /* stsd */ |
| CLEANUPonERR(M4MP4W_putBE32(a_stsdSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(SampleDescriptionHeader, |
| sizeof(SampleDescriptionHeader), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(a_esdSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| /* sample desc entry inside stsd */ |
| if (bAAC) |
| { |
| CLEANUPonERR(M4MP4W_putBlock(AACBlock1, sizeof(AACBlock1), |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*aac*/ |
| } |
| else if (bEVRC) |
| { |
| CLEANUPonERR(M4MP4W_putBlock(EVRC8Block1, sizeof(EVRC8Block1), |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*evrc*/ |
| } |
| else /*AMR8*/ |
| { |
| CLEANUPonERR(M4MP4W_putBlock(AMR8Block1, sizeof(AMR8Block1), |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*amr8*/ |
| } |
| CLEANUPonERR(M4MP4W_putBlock(SampleDescriptionEntryStart, |
| sizeof(SampleDescriptionEntryStart), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(AudioSampleDescEntryBoilerplate, |
| sizeof(AudioSampleDescEntryBoilerplate), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*audio*/ |
| CLEANUPonERR( |
| M4MP4W_putBE32(mMp4FileDataPtr->audioTrackPtr->CommonData.timescale |
| << 16, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| /* DSI inside sample desc entry */ |
| if (bAAC) |
| { |
| CLEANUPonERR(M4MP4W_putBE32(a_esdsSize, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*aac*/ |
| CLEANUPonERR(M4MP4W_putBlock(MPEGConfigBlock0, |
| sizeof(MPEGConfigBlock0), mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*aac*/ |
| CLEANUPonERR(M4MP4W_putByte(a_ESDescriptorSize, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*aac*/ |
| CLEANUPonERR(M4MP4W_putBlock(MPEGConfigBlock1, |
| sizeof(MPEGConfigBlock1), mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*aac*/ |
| CLEANUPonERR(M4MP4W_putByte(a_DCDescriptorSize, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*aac*/ |
| CLEANUPonERR(M4MP4W_putBlock(AACBlock2, sizeof(AACBlock2), |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*aac*/ |
| CLEANUPonERR( |
| M4MP4W_putBE24(mMp4FileDataPtr->audioTrackPtr->avgBitrate * 5, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*aac*/ |
| CLEANUPonERR( |
| M4MP4W_putBE32(mMp4FileDataPtr->audioTrackPtr->maxBitrate, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*aac*/ |
| CLEANUPonERR( |
| M4MP4W_putBE32(mMp4FileDataPtr->audioTrackPtr->avgBitrate, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*aac*/ |
| CLEANUPonERR(M4MP4W_putBlock(MPEGConfigBlock2, |
| sizeof(MPEGConfigBlock2), mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*aac*/ |
| CLEANUPonERR(M4MP4W_putByte(mMp4FileDataPtr->audioTrackPtr->dsiSize, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*aac*/ |
| CLEANUPonERR(M4MP4W_putBlock(mMp4FileDataPtr->audioTrackPtr->DSI, |
| mMp4FileDataPtr->audioTrackPtr->dsiSize, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*aac*/ |
| CLEANUPonERR(M4MP4W_putBlock(MPEGConfigBlock3, |
| sizeof(MPEGConfigBlock3), mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*aac*/ |
| } |
| else if (bEVRC) |
| { |
| M4OSA_UInt8 localDsi[6]; |
| M4OSA_UInt32 localI; |
| |
| CLEANUPonERR(M4MP4W_putBlock(EVRCBlock3_1, sizeof(EVRCBlock3_1), |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*audio*/ |
| |
| /* copy the default block in a local variable*/ |
| for ( localI = 0; localI < 6; localI++ ) |
| { |
| localDsi[localI] = EVRCBlock3_2[localI]; |
| } |
| /* computes the number of sample per au */ |
| /* and stores it in the DSI*/ |
| /* assumes a char is enough to store the data*/ |
| localDsi[5] = |
| (M4OSA_UInt8)(mMp4FileDataPtr->audioTrackPtr->sampleDuration |
| / 160)/*EVRC 1 frame duration*/; |
| |
| if (mMp4FileDataPtr->audioTrackPtr->DSI != M4OSA_NULL) |
| { |
| /* copy vendor name */ |
| for ( localI = 0; localI < 4; localI++ ) |
| { |
| localDsi[localI] = (M4OSA_UInt8)( |
| mMp4FileDataPtr->audioTrackPtr->DSI[localI]); |
| } |
| } |
| CLEANUPonERR(M4MP4W_putBlock(localDsi, 6, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*audio*/ |
| } |
| else /*AMR8*/ |
| { |
| M4OSA_UInt8 localDsi[9]; |
| M4OSA_UInt32 localI; |
| |
| CLEANUPonERR(M4MP4W_putBlock(AMRDSIHeader, sizeof(AMRDSIHeader), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| /* copy the default block in a local variable*/ |
| for ( localI = 0; localI < 9; localI++ ) |
| { |
| localDsi[localI] = AMRDefaultDSI[localI]; |
| } |
| /* computes the number of sample per au */ |
| /* and stores it in the DSI*/ |
| /* assumes a char is enough to store the data*/ |
| /* ALERT! The potential of the following line of code to explode in our face |
| is enormous when anything (sample rate or whatever) will change. This |
| calculation would be MUCH better handled by the VES or whatever deals with |
| the encoder more directly. */ |
| localDsi[8] = |
| (M4OSA_UInt8)(mMp4FileDataPtr->audioTrackPtr->sampleDuration |
| / 160)/*AMR NB 1 frame duration*/; |
| |
| if (mMp4FileDataPtr->audioTrackPtr->DSI != M4OSA_NULL) |
| { |
| /* copy vendor name */ |
| for ( localI = 0; localI < 4; localI++ ) |
| { |
| localDsi[localI] = (M4OSA_UInt8)( |
| mMp4FileDataPtr->audioTrackPtr->DSI[localI]); |
| } |
| |
| /* copy the Mode Set */ |
| for ( localI = 5; localI < 7; localI++ ) |
| { |
| localDsi[localI] = (M4OSA_UInt8)( |
| mMp4FileDataPtr->audioTrackPtr->DSI[localI]); |
| } |
| } |
| CLEANUPonERR(M4MP4W_putBlock(localDsi, 9, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*audio*/ |
| } |
| |
| /*end trak*/ |
| CLEANUPonERR(M4MP4W_putBE32(a_stszSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock15, sizeof(CommonBlock15), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32( |
| mMp4FileDataPtr->audioTrackPtr->CommonData.sampleSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR( |
| M4MP4W_putBE32(mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| /*0 value for samplesize means not constant AU size*/ |
| if (mMp4FileDataPtr->audioTrackPtr->CommonData.sampleSize == 0) |
| { |
| CLEANUPonERR(M4MP4W_putBlock((const M4OSA_UChar |
| *)mMp4FileDataPtr->audioTrackPtr->TABLE_STSZ, |
| mMp4FileDataPtr->audioTrackPtr->CommonData.sampleNb * 4, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| } |
| |
| CLEANUPonERR(M4MP4W_putBE32(a_stscSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock16, sizeof(CommonBlock16), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| #ifdef _M4MP4W_OPTIMIZE_FOR_PHONE |
| |
| CLEANUPonERR(M4MP4W_putBE32(mMp4FileDataPtr->audioTrackPtr->currentStsc |
| + 1, mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| for ( i = 0; i <= mMp4FileDataPtr->audioTrackPtr->currentStsc; i++ ) |
| { |
| CLEANUPonERR(M4MP4W_putBE32( |
| ( mMp4FileDataPtr->audioTrackPtr->chunkSampleNbTable[i] |
| >> 12) + 1, mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32((mMp4FileDataPtr->audioTrackPtr-> |
| chunkSampleNbTable[i] & 0xFFF), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(1, mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| } |
| |
| #else |
| |
| CLEANUPonERR(M4MP4W_putBE32(mMp4FileDataPtr->audioTrackPtr->currentChunk |
| + 1, mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| for ( i = 0; i <= mMp4FileDataPtr->audioTrackPtr->currentChunk; i++ ) |
| { |
| CLEANUPonERR(M4MP4W_putBE32(i + 1, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32( |
| mMp4FileDataPtr->audioTrackPtr->chunkSampleNbTable[i], |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(1, mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| } |
| |
| #endif |
| |
| CLEANUPonERR(M4MP4W_putBE32(a_stcoSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock17, sizeof(CommonBlock17), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(mMp4FileDataPtr->audioTrackPtr->currentChunk |
| + 1, mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| #ifdef _M4MP4W_MOOV_FIRST |
| |
| for ( i = 0; i <= mMp4FileDataPtr->audioTrackPtr->currentChunk; i++ ) |
| { |
| CLEANUPonERR(M4MP4W_putBE32(a_trakOffset, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| a_trakOffset += mMp4FileDataPtr->audioTrackPtr->chunkSizeTable[i]; |
| |
| if (( bInterleaveAV == M4OSA_TRUE) |
| && (mMp4FileDataPtr->videoTrackPtr->currentChunk >= i)) |
| { |
| a_trakOffset += |
| mMp4FileDataPtr->videoTrackPtr->chunkSizeTable[i]; |
| } |
| } |
| |
| #else |
| |
| for ( i = 0; i <= mMp4FileDataPtr->audioTrackPtr->currentChunk; i++ ) |
| { |
| CLEANUPonERR(M4MP4W_putBE32( |
| mMp4FileDataPtr->audioTrackPtr->chunkOffsetTable[i], |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| } |
| |
| #endif /*_M4MP4W_MOOV_FIRST*/ |
| |
| CLEANUPonERR(M4MP4W_putBlock(AMRBlock4, sizeof(AMRBlock4), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*audio*/ |
| } |
| |
| if (bVideo) |
| { |
| /*trak*/ |
| CLEANUPonERR(M4MP4W_putBE32(v_trakSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock6, sizeof(CommonBlock6), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(creationTime, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(creationTime, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(v_trakId, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock7, sizeof(CommonBlock7), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(v_msTrakDuration, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock7bis, sizeof(CommonBlock7bis), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| /* In the track header width and height are 16.16 fixed point values, |
| so shift left the regular integer value by 16. */ |
| CLEANUPonERR(M4MP4W_putBE32(mMp4FileDataPtr->videoTrackPtr->width << 16, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*video*/ |
| CLEANUPonERR(M4MP4W_putBE32(mMp4FileDataPtr->videoTrackPtr->height |
| << 16, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*video*/ |
| |
| CLEANUPonERR(M4MP4W_putBE32(v_mdiaSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock8, sizeof(CommonBlock8), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(creationTime, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(creationTime, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR( |
| M4MP4W_putBE32(mMp4FileDataPtr->videoTrackPtr->CommonData.timescale, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(v_trakDuration, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock9, sizeof(CommonBlock9), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(VideoBlock1_1, sizeof(VideoBlock1_1), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*video*/ |
| CLEANUPonERR(M4MP4W_putBE32(v_minfSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock10, sizeof(CommonBlock10), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(v_stblSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock11, sizeof(CommonBlock11), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(v_sttsSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock12, sizeof(CommonBlock12), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32( |
| mMp4FileDataPtr->videoTrackPtr->CommonData.sttsTableEntryNb, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| #ifdef _M4MP4W_OPTIMIZE_FOR_PHONE |
| |
| for ( i = 0; |
| i < mMp4FileDataPtr->videoTrackPtr->CommonData.sttsTableEntryNb; |
| i++ ) |
| { |
| CLEANUPonERR(M4MP4W_putBE32(M4MP4W_get32_Lo( |
| &mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[i]), |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*video*/ |
| CLEANUPonERR(M4MP4W_putBE32(M4MP4W_get32_Hi( |
| &mMp4FileDataPtr->videoTrackPtr->TABLE_STTS[i]), |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*video*/ |
| } |
| |
| #else |
| |
| CLEANUPonERR(M4MP4W_putBlock((const M4OSA_UChar |
| *)mMp4FileDataPtr->videoTrackPtr->TABLE_STTS, |
| ( mMp4FileDataPtr->videoTrackPtr->CommonData.sttsTableEntryNb) * 8, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*video*/ |
| |
| #endif |
| |
| /* stsd */ |
| |
| CLEANUPonERR(M4MP4W_putBE32(v_stsdSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(SampleDescriptionHeader, |
| sizeof(SampleDescriptionHeader), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(v_esdSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| /* sample desc entry inside stsd */ |
| if (bMP4V) |
| { |
| CLEANUPonERR(M4MP4W_putBlock(Mp4vBlock1, sizeof(Mp4vBlock1), |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*mp4v*/ |
| } |
| |
| if (bH263) |
| { |
| CLEANUPonERR(M4MP4W_putBlock(H263Block1, sizeof(H263Block1), |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*h263*/ |
| } |
| |
| if (bH264) |
| { |
| CLEANUPonERR(M4MP4W_putBlock(H264Block1, sizeof(H264Block1), |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*h264*/ |
| } |
| CLEANUPonERR(M4MP4W_putBlock(SampleDescriptionEntryStart, |
| sizeof(SampleDescriptionEntryStart), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(SampleDescriptionEntryVideoBoilerplate1, |
| sizeof(SampleDescriptionEntryVideoBoilerplate1), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*video*/ |
| CLEANUPonERR(M4MP4W_putBE16(mMp4FileDataPtr->videoTrackPtr->width, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*video*/ |
| CLEANUPonERR(M4MP4W_putBE16(mMp4FileDataPtr->videoTrackPtr->height, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*video*/ |
| CLEANUPonERR(M4MP4W_putBlock(VideoResolutions, sizeof(VideoResolutions), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*mp4v*/ |
| CLEANUPonERR(M4MP4W_putBlock(SampleDescriptionEntryVideoBoilerplate2, |
| sizeof(SampleDescriptionEntryVideoBoilerplate2), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*video*/ |
| |
| /* DSI inside sample desc entry */ |
| if (bH263) |
| { |
| /* The h263 dsi given through the api must be 7 bytes, that is, it shall not include |
| the optional bitrate box. However, if the bitrate information is set in the stream |
| handler, a bitrate box is appended here to the dsi */ |
| if (((M4OSA_Int32)mMp4FileDataPtr->videoTrackPtr->avgBitrate) != -1) |
| { |
| CLEANUPonERR(M4MP4W_putBlock(H263Block2_bitr, |
| sizeof(H263Block2_bitr), |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /* d263 box with bitr atom */ |
| |
| if (M4OSA_NULL == mMp4FileDataPtr->videoTrackPtr->DSI) |
| { |
| CLEANUPonERR(M4MP4W_putBlock(H263Block3, sizeof(H263Block3), |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*h263*/ |
| } |
| else |
| { |
| CLEANUPonERR( |
| M4MP4W_putBlock(mMp4FileDataPtr->videoTrackPtr->DSI, |
| mMp4FileDataPtr->videoTrackPtr->dsiSize, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| } |
| |
| CLEANUPonERR(M4MP4W_putBlock(H263Block4, sizeof(H263Block4), |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*h263*/ |
| /* Pierre Lebeaupin 2008/04/29: the two following lines used to be swapped; |
| I changed to this order in order to conform to 3GPP. */ |
| CLEANUPonERR( |
| M4MP4W_putBE32(mMp4FileDataPtr->videoTrackPtr->avgBitrate, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*h263*/ |
| CLEANUPonERR( |
| M4MP4W_putBE32(mMp4FileDataPtr->videoTrackPtr->maxBitrate, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*h263*/ |
| } |
| else |
| { |
| CLEANUPonERR(M4MP4W_putBlock(H263Block2, sizeof(H263Block2), |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /* d263 box */ |
| |
| if (M4OSA_NULL == mMp4FileDataPtr->videoTrackPtr->DSI) |
| { |
| CLEANUPonERR(M4MP4W_putBlock(H263Block3, sizeof(H263Block3), |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*h263*/ |
| } |
| else |
| { |
| CLEANUPonERR( |
| M4MP4W_putBlock(mMp4FileDataPtr->videoTrackPtr->DSI, |
| mMp4FileDataPtr->videoTrackPtr->dsiSize, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| } |
| } |
| } |
| |
| if (bMP4V) |
| { |
| M4OSA_UInt32 bufferSizeDB = 5 * mMp4FileDataPtr->videoTrackPtr-> |
| avgBitrate; /*bufferSizeDB set to 5 times the bitrate*/ |
| |
| CLEANUPonERR(M4MP4W_putBE32(v_esdsSize, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*mp4v*/ |
| CLEANUPonERR(M4MP4W_putBlock(MPEGConfigBlock0, |
| sizeof(MPEGConfigBlock0), mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*mp4v*/ |
| CLEANUPonERR(M4MP4W_putByte(v_ESDescriptorSize, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*mp4v*/ |
| CLEANUPonERR(M4MP4W_putBlock(MPEGConfigBlock1, |
| sizeof(MPEGConfigBlock1), mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*mp4v*/ |
| CLEANUPonERR(M4MP4W_putByte(v_DCDescriptorSize, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*mp4v*/ |
| CLEANUPonERR(M4MP4W_putBlock(Mp4vBlock3, sizeof(Mp4vBlock3), |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*mp4v*/ |
| CLEANUPonERR(M4MP4W_putBE24(bufferSizeDB, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*mp4v*/ |
| CLEANUPonERR( |
| M4MP4W_putBE32(mMp4FileDataPtr->videoTrackPtr->maxBitrate, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*mp4v*/ |
| CLEANUPonERR( |
| M4MP4W_putBE32(mMp4FileDataPtr->videoTrackPtr->avgBitrate, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*mp4v*/ |
| CLEANUPonERR(M4MP4W_putBlock(MPEGConfigBlock2, |
| sizeof(MPEGConfigBlock2), mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*mp4v*/ |
| CLEANUPonERR(M4MP4W_putByte(mMp4FileDataPtr->videoTrackPtr->dsiSize, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*mp4v*/ |
| CLEANUPonERR(M4MP4W_putBlock(mMp4FileDataPtr->videoTrackPtr->DSI, |
| mMp4FileDataPtr->videoTrackPtr->dsiSize, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*mp4v*/ |
| CLEANUPonERR(M4MP4W_putBlock(MPEGConfigBlock3, |
| sizeof(MPEGConfigBlock3), mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*mp4v*/ |
| } |
| |
| if (bH264) |
| { |
| M4OSA_UInt16 ppsLentgh = 0; /* PPS length */ |
| M4OSA_UInt16 spsLentgh = 0; /* SPS length */ |
| M4OSA_UChar *tmpDSI = mMp4FileDataPtr->videoTrackPtr->DSI; /* DSI */ |
| M4OSA_UInt16 NumberOfPPS; |
| M4OSA_UInt16 lCntPPS; |
| |
| /* Put the avcC (header + DSI) size */ |
| CLEANUPonERR(M4MP4W_putBE32(v_avcCSize, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*h264*/ |
| /* Put the avcC header */ |
| CLEANUPonERR(M4MP4W_putBlock(H264Block2, sizeof(H264Block2), |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*h264*/ |
| /* Put the DSI (SPS + PPS) int the 3gp format*/ |
| /* SPS length in BE */ |
| |
| if ((0x01 != mMp4FileDataPtr->videoTrackPtr->DSI[0]) || |
| (0x42 != mMp4FileDataPtr->videoTrackPtr->DSI[1])) |
| { |
| M4OSA_TRACE1_2("!!! M4MP4W_closeWrite ERROR : invalid AVCC 0x%X 0x%X", |
| mMp4FileDataPtr->videoTrackPtr->DSI[0], |
| mMp4FileDataPtr->videoTrackPtr->DSI[1]); |
| return M4ERR_PARAMETER; |
| } |
| // Do not strip the DSI |
| CLEANUPonERR( M4MP4W_putBlock(mMp4FileDataPtr->videoTrackPtr->DSI, |
| mMp4FileDataPtr->videoTrackPtr->dsiSize, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext) );/*h264*/ |
| |
| } |
| |
| /*end trak*/ |
| CLEANUPonERR(M4MP4W_putBE32(v_stszSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock15, sizeof(CommonBlock15), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32( |
| mMp4FileDataPtr->videoTrackPtr->CommonData.sampleSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR( |
| M4MP4W_putBE32(mMp4FileDataPtr->videoTrackPtr->CommonData.sampleNb, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| #ifdef _M4MP4W_OPTIMIZE_FOR_PHONE |
| |
| for ( i = 0; i < mMp4FileDataPtr->videoTrackPtr->CommonData.sampleNb; |
| i++ ) |
| { |
| CLEANUPonERR( |
| M4MP4W_putBE32(mMp4FileDataPtr->videoTrackPtr->TABLE_STSZ[i], |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*video*/ |
| } |
| |
| #else |
| |
| CLEANUPonERR(M4MP4W_putBlock((const M4OSA_UChar |
| *)mMp4FileDataPtr->videoTrackPtr->TABLE_STSZ, |
| mMp4FileDataPtr->videoTrackPtr->CommonData.sampleNb * 4, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*video*/ |
| |
| #endif |
| |
| CLEANUPonERR(M4MP4W_putBE32(v_stscSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock16, sizeof(CommonBlock16), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| #ifdef _M4MP4W_OPTIMIZE_FOR_PHONE |
| |
| CLEANUPonERR(M4MP4W_putBE32(mMp4FileDataPtr->videoTrackPtr->currentStsc |
| + 1, mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| for ( i = 0; i <= mMp4FileDataPtr->videoTrackPtr->currentStsc; i++ ) |
| { |
| CLEANUPonERR(M4MP4W_putBE32( |
| ( mMp4FileDataPtr->videoTrackPtr->chunkSampleNbTable[i] |
| >> 12) + 1, mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32((mMp4FileDataPtr->videoTrackPtr-> |
| chunkSampleNbTable[i] & 0xFFF), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(1, mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| } |
| |
| #else |
| |
| CLEANUPonERR(M4MP4W_putBE32(mMp4FileDataPtr->videoTrackPtr->currentChunk |
| + 1, mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| for (i = 0; i <= mMp4FileDataPtr->videoTrackPtr->currentChunk; i++) |
| { |
| CLEANUPonERR(M4MP4W_putBE32(i + 1, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32( |
| mMp4FileDataPtr->videoTrackPtr->chunkSampleNbTable[i], |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(1, mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| } |
| |
| #endif |
| |
| CLEANUPonERR(M4MP4W_putBE32(v_stcoSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock17, sizeof(CommonBlock17), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBE32(mMp4FileDataPtr->videoTrackPtr->currentChunk |
| + 1, mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| #ifdef _M4MP4W_MOOV_FIRST |
| |
| for (i = 0; i <= mMp4FileDataPtr->videoTrackPtr->currentChunk; i++) |
| { |
| if (( bInterleaveAV == M4OSA_TRUE) |
| && (mMp4FileDataPtr->audioTrackPtr->currentChunk >= i)) |
| { |
| v_trakOffset += |
| mMp4FileDataPtr->audioTrackPtr->chunkSizeTable[i]; |
| } |
| CLEANUPonERR(M4MP4W_putBE32(v_trakOffset, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| v_trakOffset += mMp4FileDataPtr->videoTrackPtr->chunkSizeTable[i]; |
| } |
| |
| #else |
| |
| for ( i = 0; i <= mMp4FileDataPtr->videoTrackPtr->currentChunk; i++ ) |
| { |
| CLEANUPonERR(M4MP4W_putBE32( |
| mMp4FileDataPtr->videoTrackPtr->chunkOffsetTable[i], |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| } |
| |
| #endif /*_M4MP4W_MOOV_FIRST*/ |
| |
| CLEANUPonERR(M4MP4W_putBE32(v_stssSize, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*video*/ |
| CLEANUPonERR(M4MP4W_putBlock(VideoBlock4, sizeof(VideoBlock4), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*video*/ |
| CLEANUPonERR( |
| M4MP4W_putBE32(mMp4FileDataPtr->videoTrackPtr->stssTableEntryNb, |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*video*/ |
| CLEANUPonERR(M4MP4W_putBlock((const M4OSA_UChar |
| *)mMp4FileDataPtr->videoTrackPtr->TABLE_STSS, |
| mMp4FileDataPtr->videoTrackPtr->stssTableEntryNb * 4, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*video*/ |
| CLEANUPonERR(M4MP4W_putBlock(VideoBlock5, sizeof(VideoBlock5), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); /*video*/ |
| } |
| #ifdef _M4MP4W_MOOV_FIRST |
| /*mdat*/ |
| |
| CLEANUPonERR(M4MP4W_putBE32(mdatSize, mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putBlock(CommonBlock2, sizeof(CommonBlock2), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| /*write data, according to the interleave mode (default is not interleaved)*/ |
| if (bInterleaveAV == M4OSA_FALSE) |
| { |
| if (bAudio) |
| { |
| for ( i = 0; i <= mMp4FileDataPtr->audioTrackPtr->currentChunk; |
| i++ ) |
| { |
| CLEANUPonERR( |
| M4MP4W_putBlock(mMp4FileDataPtr->audioTrackPtr->Chunk[i], |
| mMp4FileDataPtr->audioTrackPtr->chunkSizeTable[i], |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*audio (previously a_dataSize)*/ |
| } |
| } |
| |
| if (bVideo) |
| { |
| for ( i = 0; i <= mMp4FileDataPtr->videoTrackPtr->currentChunk; |
| i++ ) |
| { |
| CLEANUPonERR( |
| M4MP4W_putBlock(mMp4FileDataPtr->videoTrackPtr->Chunk[i], |
| mMp4FileDataPtr->videoTrackPtr->chunkSizeTable[i], |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*video (previously a_dataSize)*/ |
| } |
| } |
| } |
| else /*in this mode, we have audio and video to interleave*/ |
| { |
| for ( i = 0; i <= max(mMp4FileDataPtr->audioTrackPtr->currentChunk, |
| mMp4FileDataPtr->videoTrackPtr->currentChunk); i++ ) |
| { |
| if (i <= mMp4FileDataPtr->audioTrackPtr->currentChunk) |
| { |
| CLEANUPonERR( |
| M4MP4W_putBlock(mMp4FileDataPtr->audioTrackPtr->Chunk[i], |
| mMp4FileDataPtr->audioTrackPtr->chunkSizeTable[i], |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*audio (previously a_dataSize)*/ |
| } |
| |
| if (i <= mMp4FileDataPtr->videoTrackPtr->currentChunk) |
| { |
| CLEANUPonERR( |
| M4MP4W_putBlock(mMp4FileDataPtr->videoTrackPtr->Chunk[i], |
| mMp4FileDataPtr->videoTrackPtr->chunkSizeTable[i], |
| mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); /*video (previously a_dataSize)*/ |
| } |
| } |
| } |
| |
| #endif /*_M4MP4W_MOOV_FIRST*/ |
| |
| /*skip*/ |
| |
| CLEANUPonERR(M4MP4W_putBlock(BlockSignatureSkipHeader, |
| sizeof(BlockSignatureSkipHeader), mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| |
| /* Write embedded string */ |
| if (mMp4FileDataPtr->embeddedString == M4OSA_NULL) |
| { |
| CLEANUPonERR(M4MP4W_putBlock(BlockSignatureSkipDefaultEmbeddedString, |
| sizeof(BlockSignatureSkipDefaultEmbeddedString), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| } |
| else |
| { |
| CLEANUPonERR(M4MP4W_putBlock(mMp4FileDataPtr->embeddedString, 16, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| } |
| |
| /* Write ves core version */ |
| camcoder_maj = (M4OSA_UChar)(mMp4FileDataPtr->camcoderVersion / 100); |
| camcoder_min = |
| (M4OSA_UChar)(( mMp4FileDataPtr->camcoderVersion - 100 * camcoder_maj) |
| / 10); |
| camcoder_rev = |
| (M4OSA_UChar)(mMp4FileDataPtr->camcoderVersion - 100 * camcoder_maj - 10 |
| * camcoder_min); |
| |
| CLEANUPonERR(M4MP4W_putByte(' ', mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putByte((M4OSA_UChar)(camcoder_maj + '0'), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putByte('.', mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putByte((M4OSA_UChar)(camcoder_min + '0'), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putByte('.', mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| CLEANUPonERR(M4MP4W_putByte((M4OSA_UChar)(camcoder_rev + '0'), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| /* Write integration tag */ |
| CLEANUPonERR(M4MP4W_putBlock((const M4OSA_UChar *)" -- ", 4, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| |
| if (mMp4FileDataPtr->integrationTag == M4OSA_NULL) |
| { |
| CLEANUPonERR(M4MP4W_putBlock(BlockSignatureSkipDefaultIntegrationTag, |
| sizeof(BlockSignatureSkipDefaultIntegrationTag), |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| } |
| else |
| { |
| CLEANUPonERR(M4MP4W_putBlock(mMp4FileDataPtr->integrationTag, 60, |
| mMp4FileDataPtr->fileWriterFunctions, fileWriterContext)); |
| } |
| |
| #ifndef _M4MP4W_MOOV_FIRST |
| /*overwrite mdat size*/ |
| |
| if (mMp4FileDataPtr->ftyp.major_brand != 0) |
| mdatPos= 16 + mMp4FileDataPtr->ftyp.nbCompatibleBrands * 4; |
| else |
| mdatPos = 24; |
| |
| moovPos = moovPos - mdatPos; |
| mdatSize = moovPos; |
| |
| CLEANUPonERR(mMp4FileDataPtr->fileWriterFunctions->seek(fileWriterContext, |
| M4OSA_kFileSeekBeginning, &mdatPos)); /*seek after ftyp...*/ |
| CLEANUPonERR(M4MP4W_putBE32(mdatSize, mMp4FileDataPtr->fileWriterFunctions, |
| fileWriterContext)); |
| |
| #endif /*_M4MP4W_MOOV_FIRST*/ |
| |
| cleanup: |
| |
| /** |
| * Close the file even if an error occured */ |
| if (M4OSA_NULL != mMp4FileDataPtr->fileWriterContext) |
| { |
| err2 = |
| mMp4FileDataPtr->fileWriterFunctions->closeWrite(mMp4FileDataPtr-> |
| fileWriterContext); /**< close the stream anyway */ |
| |
| if (M4NO_ERROR != err2) |
| { |
| M4OSA_TRACE1_1( |
| "M4MP4W_closeWrite: fileWriterFunctions->closeWrite returns 0x%x", |
| err2); |
| } |
| mMp4FileDataPtr->fileWriterContext = M4OSA_NULL; |
| } |
| |
| #ifdef _M4MP4W_RESERVED_MOOV_DISK_SPACE |
| /* Remove safety file if still present (here it is cleanup in case of error and NOT the normal |
| removal of the safety file to free emergency space for the moov). */ |
| |
| if (M4OSA_TRUE == mMp4FileDataPtr->cleanSafetyFile) |
| { |
| M4OSA_Context tempContext; |
| err3 = mMp4FileDataPtr->fileWriterFunctions->openWrite(&tempContext, |
| mMp4FileDataPtr->safetyFileUrl, |
| M4OSA_kFileWrite | M4OSA_kFileCreate); |
| |
| if (M4NO_ERROR != err2) |
| err2 = err3; |
| |
| if (M4NO_ERROR |
| != err3) /* No sense closing if we couldn't open in the first place. */ |
| { |
| err3 = |
| mMp4FileDataPtr->fileWriterFunctions->closeWrite(tempContext); |
| |
| if (M4NO_ERROR != err2) |
| err2 = err3; |
| } |
| mMp4FileDataPtr->safetyFileUrl = M4OSA_NULL; |
| mMp4FileDataPtr->cleanSafetyFile = M4OSA_FALSE; |
| } |
| |
| #endif /* _M4MP4W_RESERVED_MOOV_DISK_SPACE */ |
| |
| /* Delete embedded string */ |
| |
| if (M4OSA_NULL != mMp4FileDataPtr->embeddedString) |
| { |
| free(mMp4FileDataPtr->embeddedString); |
| mMp4FileDataPtr->embeddedString = M4OSA_NULL; |
| } |
| |
| /* Delete integration tag */ |
| if (M4OSA_NULL != mMp4FileDataPtr->integrationTag) |
| { |
| free(mMp4FileDataPtr->integrationTag); |
| mMp4FileDataPtr->integrationTag = M4OSA_NULL; |
| } |
| |
| /** |
| * M4MP4W_freeContext() is now a private method, called only from here*/ |
| err3 = M4MP4W_freeContext(context); |
| |
| if (M4NO_ERROR != err3) |
| { |
| M4OSA_TRACE1_1("M4MP4W_closeWrite: M4MP4W_freeContext returns 0x%x", |
| err3); |
| } |
| |
| /** |
| * Choose which error code to return */ |
| if (M4NO_ERROR != err) |
| { |
| /** |
| * We give priority to main error */ |
| M4OSA_TRACE1_1("M4MP4W_closeWrite: returning err=0x%x", err); |
| return err; |
| } |
| else if (M4NO_ERROR != err2) |
| { |
| /** |
| * Error from closeWrite is returned if there is no main error */ |
| M4OSA_TRACE1_1("M4MP4W_closeWrite: returning err2=0x%x", err2); |
| return err2; |
| } |
| else |
| { |
| /** |
| * Error from M4MP4W_freeContext is returned only if there is no main error and |
| no close error */ |
| M4OSA_TRACE1_1("M4MP4W_closeWrite: returning err3=0x%x", err3); |
| return err3; |
| } |
| } |
| |
| /*******************************************************************************/ |
| M4OSA_ERR M4MP4W_getOption( M4OSA_Context context, M4OSA_OptionID option, |
| M4OSA_DataOption *valuePtr ) |
| /*******************************************************************************/ |
| { |
| M4OSA_ERR err = M4NO_ERROR; |
| |
| M4SYS_StreamIDValue *streamIDvaluePtr = M4OSA_NULL; |
| M4MP4W_StreamIDsize *streamIDsizePtr = M4OSA_NULL; |
| M4MP4W_memAddr *memAddrPtr = M4OSA_NULL; |
| /* M4MP4W_WriteCallBack* callBackPtr = M4OSA_NULL;*/ |
| |
| M4MP4W_Mp4FileData *mMp4FileDataPtr = (M4MP4W_Mp4FileData *)context; |
| ERR_CHECK(context != M4OSA_NULL, M4ERR_PARAMETER); |
| |
| ERR_CHECK(( mMp4FileDataPtr->state == M4MP4W_opened) |
| || (mMp4FileDataPtr->state == M4MP4W_ready), M4ERR_STATE); |
| |
| switch( option ) |
| { |
| case (M4MP4W_maxAUperChunk): |
| return M4ERR_NOT_IMPLEMENTED; |
| |
| case (M4MP4W_maxChunkSize): |
| |
| streamIDvaluePtr = (M4SYS_StreamIDValue *)(*valuePtr); |
| |
| switch( streamIDvaluePtr->streamID ) |
| { |
| case (AudioStreamID): |
| if (mMp4FileDataPtr->hasAudio == M4OSA_FALSE) |
| return M4ERR_BAD_STREAM_ID; |
| else |
| streamIDvaluePtr->value = |
| mMp4FileDataPtr->audioTrackPtr->MaxChunkSize; |
| break; |
| |
| case (VideoStreamID): |
| if (mMp4FileDataPtr->hasVideo == M4OSA_FALSE) |
| return M4ERR_BAD_STREAM_ID; |
| else |
| streamIDvaluePtr->value = |
| mMp4FileDataPtr->videoTrackPtr->MaxChunkSize; |
| break; |
| |
| case (0): /*all streams*/ |
| streamIDvaluePtr->value = mMp4FileDataPtr->MaxChunkSize; |
| break; |
| |
| default: |
| return M4ERR_BAD_STREAM_ID; |
| } |
| |
| break; |
| |
| case (M4MP4W_maxChunkInter): |
| |
| streamIDvaluePtr = (M4SYS_StreamIDValue *)(*valuePtr); |
| |
| switch( streamIDvaluePtr->streamID ) |
| { |
| case (0): /*all streams*/ |
| streamIDvaluePtr->value = (M4OSA_UInt32)mMp4FileDataPtr-> |
| InterleaveDur; /*time conversion !*/ |
| break; |
| |
| default: |
| return M4ERR_BAD_STREAM_ID; |
| } |
| break; |
| |
| case (M4MP4W_embeddedString): |
| memAddrPtr = (M4MP4W_memAddr *)(*valuePtr); |
| /*memAddrPtr must have been already allocated by the caller |
| and memAddrPtr->size initialized with the max possible length in bytes*/ |
| ERR_CHECK(memAddrPtr->size >= 16, M4ERR_PARAMETER); |
| ERR_CHECK(memAddrPtr->addr != M4OSA_NULL, M4ERR_PARAMETER); |
| /*memAddrPtr->size is updated with the actual size of the string*/ |
| memAddrPtr->size = 16; |
| /*if no value was set, return the default string */ |
| if (mMp4FileDataPtr->embeddedString != M4OSA_NULL) |
| memcpy((void *)memAddrPtr->addr, |
| (void *)mMp4FileDataPtr->embeddedString, 16); |
| else |
| memcpy((void *)memAddrPtr->addr, |
| (void *)BlockSignatureSkipDefaultEmbeddedString, |
| 16); |
| break; |
| |
| case (M4MP4W_integrationTag): |
| memAddrPtr = (M4MP4W_memAddr *)(*valuePtr); |
| /*memAddrPtr must have been already allocated by the caller |
| and memAddrPtr->size initialized with the max possible length in bytes*/ |
| ERR_CHECK(memAddrPtr->size >= 60, M4ERR_PARAMETER); |
| ERR_CHECK(memAddrPtr->addr != M4OSA_NULL, M4ERR_PARAMETER); |
| /*memAddrPtr->size is updated with the actual size of the string*/ |
| memAddrPtr->size = 60; |
| /*if no value was set, return the default string 0 */ |
| if (mMp4FileDataPtr->integrationTag != M4OSA_NULL) |
| memcpy((void *)memAddrPtr->addr, |
| (void *)mMp4FileDataPtr->integrationTag, 60); |
| else |
| memcpy((void *)memAddrPtr->addr, |
| (void *)BlockSignatureSkipDefaultIntegrationTag, |
| 60); |
| break; |
| |
| case (M4MP4W_CamcoderVersion): |
| |
| streamIDvaluePtr = (M4SYS_StreamIDValue *)(*valuePtr); |
| |
| switch( streamIDvaluePtr->streamID ) |
| { |
| case (0): /*all streams*/ |
| streamIDvaluePtr->value = mMp4FileDataPtr->camcoderVersion; |
| break; |
| |
| default: |
| return M4ERR_BAD_STREAM_ID; |
| } |
| break; |
| |
| case (M4MP4W_preWriteCallBack): |
| return M4ERR_NOT_IMPLEMENTED; |
| /*callBackPtr = (M4MP4W_WriteCallBack*)(*valuePtr); |
| *callBackPtr = mMp4FileDataPtr->PreWriteCallBack; |
| break;*/ |
| |
| case (M4MP4W_postWriteCallBack): |
| return M4ERR_NOT_IMPLEMENTED; |
| /*callBackPtr = (M4MP4W_WriteCallBack*)(*valuePtr); |
| *callBackPtr = mMp4FileDataPtr->PostWriteCallBack; |
| break;*/ |
| |
| case (M4MP4W_maxAUsize): |
| |
| streamIDvaluePtr = (M4SYS_StreamIDValue *)(*valuePtr); |
| |
| switch( streamIDvaluePtr->streamID ) |
| { |
| case (AudioStreamID): |
| if (mMp4FileDataPtr->hasAudio == M4OSA_FALSE) |
| return M4ERR_BAD_STREAM_ID; |
| else |
| streamIDvaluePtr->value = |
| mMp4FileDataPtr->audioTrackPtr->MaxAUSize; |
| break; |
| |
| case (VideoStreamID): |
| if (mMp4FileDataPtr->hasVideo == M4OSA_FALSE) |
| return M4ERR_BAD_STREAM_ID; |
| else |
| streamIDvaluePtr->value = |
| mMp4FileDataPtr->videoTrackPtr->MaxAUSize; |
| break; |
| |
| case (0): /*all streams*/ |
| streamIDvaluePtr->value = mMp4FileDataPtr->MaxAUSize; |
| break; |
| |
| default: |
| return M4ERR_BAD_STREAM_ID; |
| } |
| |
| break; |
| |
| case (M4MP4W_IOD): |
| return M4ERR_NOT_IMPLEMENTED; |
| |
| case (M4MP4W_ESD): |
| return M4ERR_NOT_IMPLEMENTED; |
| |
| case (M4MP4W_SDP): |
| return M4ERR_NOT_IMPLEMENTED; |
| |
| case (M4MP4W_trackSize): |
| streamIDsizePtr = (M4MP4W_StreamIDsize *)(*valuePtr); |
| streamIDsizePtr->width = mMp4FileDataPtr->videoTrackPtr->width; |
| streamIDsizePtr->height = mMp4FileDataPtr->videoTrackPtr->height; |
| break; |
| |
| case (M4MP4W_estimateAudioSize): |
| streamIDvaluePtr = (M4SYS_StreamIDValue *)(*valuePtr); |
| streamIDvaluePtr->value = |
| (M4OSA_UInt32)mMp4FileDataPtr->estimateAudioSize; |
| break; |
| |
| case (M4MP4W_MOOVfirst): |
| return M4ERR_NOT_IMPLEMENTED; |
| |
| case (M4MP4W_V2_MOOF): |
| return M4ERR_NOT_IMPLEMENTED; |
| |
| case (M4MP4W_V2_tblCompres): |
| return M4ERR_NOT_IMPLEMENTED; |
| |
| default: |
| return M4ERR_BAD_OPTION_ID; |
| } |
| |
| return err; |
| } |
| |
| /*******************************************************************************/ |
| M4OSA_ERR M4MP4W_setOption( M4OSA_Context context, M4OSA_OptionID option, |
| M4OSA_DataOption value ) |
| /*******************************************************************************/ |
| { |
| M4OSA_ERR err = M4NO_ERROR; |
| |
| M4SYS_StreamIDValue *streamIDvaluePtr = M4OSA_NULL; |
| M4MP4W_StreamIDsize *streamIDsizePtr = M4OSA_NULL; |
| M4MP4W_memAddr *memAddrPtr = M4OSA_NULL; |
| M4SYS_StreamIDmemAddr *streamIDmemAddrPtr; |
| |
| M4MP4W_Mp4FileData *mMp4FileDataPtr = (M4MP4W_Mp4FileData *)context; |
| ERR_CHECK(context != M4OSA_NULL, M4ERR_PARAMETER); |
| |
| /* Verify state */ |
| switch( option ) |
| { |
| case M4MP4W_maxFileDuration: |
| case M4MP4W_DSI: |
| /* this param can be set at the end of a recording */ |
| ERR_CHECK((mMp4FileDataPtr->state != M4MP4W_closed), M4ERR_STATE); |
| break; |
| |
| case M4MP4W_setFtypBox: |
| /* this param can only be set before starting any write */ |
| ERR_CHECK(mMp4FileDataPtr->state == M4MP4W_opened, M4ERR_STATE); |
| break; |
| |
| default: |
| /* in general params can be set at open or ready stage */ |
| ERR_CHECK(( mMp4FileDataPtr->state == M4MP4W_opened) |
| || (mMp4FileDataPtr->state == M4MP4W_ready), M4ERR_STATE); |
| } |
| |
| /* Set option */ |
| switch( option ) |
| { |
| case (M4MP4W_maxAUperChunk): |
| return M4ERR_NOT_IMPLEMENTED; |
| |
| case (M4MP4W_maxChunkSize): |
| |
| streamIDvaluePtr = (M4SYS_StreamIDValue *)value; |
| |
| switch( streamIDvaluePtr->streamID ) |
| { |
| case (AudioStreamID): |
| if (mMp4FileDataPtr->hasAudio == M4OSA_FALSE) |
| return |
| M4ERR_BAD_STREAM_ID; /*maybe the stream has not been added yet*/ |
| else |
| { |
| mMp4FileDataPtr->audioTrackPtr->MaxChunkSize = |
| streamIDvaluePtr->value; |
| } |
| |
| break; |
| |
| case (VideoStreamID): |
| if (mMp4FileDataPtr->hasVideo == M4OSA_FALSE) |
| return |
| M4ERR_BAD_STREAM_ID; /*maybe the stream has not been added yet*/ |
| else |
| { |
| mMp4FileDataPtr->videoTrackPtr->MaxChunkSize = |
| streamIDvaluePtr->value; |
| } |
| break; |
| |
| case (0): /*all streams*/ |
| |
| /*In M4MP4W_opened state, no stream is present yet, so only global value |
| needs to be updated.*/ |
| mMp4FileDataPtr->MaxChunkSize = streamIDvaluePtr->value; |
| |
| if (mMp4FileDataPtr->hasAudio == M4OSA_TRUE) |
| { |
| mMp4FileDataPtr->audioTrackPtr->MaxChunkSize = |
| streamIDvaluePtr->value; |
| } |
| |
| if (mMp4FileDataPtr->hasVideo == M4OSA_TRUE) |
| { |
| mMp4FileDataPtr->videoTrackPtr->MaxChunkSize = |
| streamIDvaluePtr->value; |
| } |
| break; |
| |
| default: |
| return M4ERR_BAD_STREAM_ID; |
| } |
| break; |
| |
| case (M4MP4W_maxChunkInter): |
| |
| streamIDvaluePtr = (M4SYS_StreamIDValue *)value; |
| |
| switch( streamIDvaluePtr->streamID ) |
| { |
| case (0): /*all streams*/ |
| mMp4FileDataPtr->InterleaveDur = |
| (M4MP4W_Time32)streamIDvaluePtr-> |
| value; /*time conversion!*/ |
| break; |
| |
| default: |
| return M4ERR_BAD_STREAM_ID; |
| /*not meaningfull to set this parameter on a streamID basis*/ |
| } |
| break; |
| |
| case (M4MP4W_maxFileSize): |
| mMp4FileDataPtr->MaxFileSize = *(M4OSA_UInt32 *)value; |
| break; |
| |
| case (M4MP4W_embeddedString): |
| memAddrPtr = (M4MP4W_memAddr *)value; |
| /* |
| * If memAddrPtr->size > 16 bytes, then the string will be truncated. |
| * If memAddrPtr->size < 16 bytes, then return M4ERR_PARAMETER |
| */ |
| ERR_CHECK(memAddrPtr->size >= 16, M4ERR_PARAMETER); |
| |
| if (mMp4FileDataPtr->embeddedString == M4OSA_NULL) |
| { |
| mMp4FileDataPtr->embeddedString = |
| (M4OSA_UChar *)M4OSA_32bitAlignedMalloc(16, M4MP4_WRITER, |
| (M4OSA_Char *)"embeddedString"); |
| ERR_CHECK(mMp4FileDataPtr->embeddedString != M4OSA_NULL, |
| M4ERR_ALLOC); |
| } |
| /*else, just overwrite the previously set string*/ |
| memcpy((void *)mMp4FileDataPtr->embeddedString, |
| (void *)memAddrPtr->addr, 16); |
| break; |
| |
| case (M4MP4W_integrationTag): |
| memAddrPtr = (M4MP4W_memAddr *)value; |
| /* |
| * If memAddrPtr->size > 60 bytes, then the string will be truncated. |
| * If memAddrPtr->size < 60 bytes, then pad with 0 |
| */ |
| if (mMp4FileDataPtr->integrationTag == M4OSA_NULL) |
| { |
| mMp4FileDataPtr->integrationTag = |
| (M4OSA_UChar *)M4OSA_32bitAlignedMalloc(60, M4MP4_WRITER, |
| (M4OSA_Char *)"integrationTag"); |
| ERR_CHECK(mMp4FileDataPtr->integrationTag != M4OSA_NULL, |
| M4ERR_ALLOC); |
| } |
| /*else, just overwrite the previously set string*/ |
| if (memAddrPtr->size < 60) |
| { |
| memcpy((void *)mMp4FileDataPtr->integrationTag, |
| (void *)BlockSignatureSkipDefaultIntegrationTag, |
| 60); |
| memcpy((void *)mMp4FileDataPtr->integrationTag, |
| (void *)memAddrPtr->addr, memAddrPtr->size); |
| } |
| else |
| { |
| memcpy((void *)mMp4FileDataPtr->integrationTag, |
| (void *)memAddrPtr->addr, 60); |
| } |
| break; |
| |
| case (M4MP4W_CamcoderVersion): |
| |
| streamIDvaluePtr = (M4SYS_StreamIDValue *)value; |
| |
| switch( streamIDvaluePtr->streamID ) |
| { |
| case (0): /*all streams*/ |
| mMp4FileDataPtr->camcoderVersion = streamIDvaluePtr->value; |
| break; |
| |
| default: |
| return M4ERR_BAD_STREAM_ID; |
| /*not meaningfull to set this parameter on a streamID basis*/ |
| } |
| break; |
| |
| case (M4MP4W_preWriteCallBack): |
| return M4ERR_NOT_IMPLEMENTED; |
| /*mMp4FileDataPtr->PreWriteCallBack = *(M4MP4W_WriteCallBack*)value; |
| break;*/ |
| |
| case (M4MP4W_postWriteCallBack): |
| return M4ERR_NOT_IMPLEMENTED; |
| /*mMp4FileDataPtr->PostWriteCallBack = *(M4MP4W_WriteCallBack*)value; |
| break;*/ |
| |
| case (M4MP4W_maxAUsize): |
| |
| streamIDvaluePtr = (M4SYS_StreamIDValue *)value; |
| |
| switch( streamIDvaluePtr->streamID ) |
| { |
| case (AudioStreamID): |
| |
| /*if (mMp4FileDataPtr->audioTrackPtr == M4OSA_NULL)*/ |
| if (mMp4FileDataPtr->hasAudio == M4OSA_FALSE) |
| return M4ERR_BAD_STREAM_ID; |
| else |
| mMp4FileDataPtr->audioTrackPtr->MaxAUSize = |
| streamIDvaluePtr->value; |
| break; |
| |
| case (VideoStreamID): |
| |
| /*if (mMp4FileDataPtr->videoTrackPtr == M4OSA_NULL)*/ |
| if (mMp4FileDataPtr->hasVideo == M4OSA_FALSE) |
| return M4ERR_BAD_STREAM_ID; |
| else |
| mMp4FileDataPtr->videoTrackPtr->MaxAUSize = |
| streamIDvaluePtr->value; |
| break; |
| |
| case (0): /*all streams*/ |
| |
| mMp4FileDataPtr->MaxAUSize = streamIDvaluePtr->value; |
| |
| if (mMp4FileDataPtr->hasAudio == M4OSA_TRUE) |
| mMp4FileDataPtr->audioTrackPtr->MaxAUSize = |
| streamIDvaluePtr->value; |
| |
| if (mMp4FileDataPtr->hasVideo == M4OSA_TRUE) |
| mMp4FileDataPtr->videoTrackPtr->MaxAUSize = |
| streamIDvaluePtr->value; |
| |
| break; |
| |
| default: |
| return M4ERR_BAD_STREAM_ID; |
| } |
| break; |
| |
| case (M4MP4W_IOD): |
| return M4ERR_NOT_IMPLEMENTED; |
| |
| case (M4MP4W_ESD): |
| return M4ERR_NOT_IMPLEMENTED; |
| |
| case (M4MP4W_SDP): |
| return M4ERR_NOT_IMPLEMENTED; |
| |
| case (M4MP4W_trackSize): |
| |
| streamIDsizePtr = (M4MP4W_StreamIDsize *)value; |
| |
| if ((streamIDsizePtr->streamID != VideoStreamID) |
| || (mMp4FileDataPtr->hasVideo == M4OSA_FALSE)) |
| return M4ERR_BAD_STREAM_ID; |
| else |
| { |
| mMp4FileDataPtr->videoTrackPtr->width = streamIDsizePtr->width; |
| mMp4FileDataPtr->videoTrackPtr->height = |
| streamIDsizePtr->height; |
| } |
| break; |
| |
| case (M4MP4W_estimateAudioSize): |
| |
| streamIDvaluePtr = (M4SYS_StreamIDValue *)value; |
| |
| /*shall not set this option before audio and video streams were added*/ |
| /*nonsense to set this option if not in case audio+video*/ |
| if ((mMp4FileDataPtr->hasAudio == M4OSA_FALSE) |
| || (mMp4FileDataPtr->hasVideo == M4OSA_FALSE)) |
| return M4ERR_STATE; |
| |
| mMp4FileDataPtr->estimateAudioSize = |
| (M4OSA_Bool)streamIDvaluePtr->value; |
| break; |
| |
| case (M4MP4W_MOOVfirst): |
| return M4ERR_NOT_IMPLEMENTED; |
| |
| case (M4MP4W_V2_MOOF): |
| return M4ERR_NOT_IMPLEMENTED; |
| |
| case (M4MP4W_V2_tblCompres): |
| return M4ERR_NOT_IMPLEMENTED; |
| |
| case (M4MP4W_maxFileDuration): |
| mMp4FileDataPtr->MaxFileDuration = *(M4OSA_UInt32 *)value; |
| break; |
| |
| case (M4MP4W_setFtypBox): |
| { |
| M4OSA_UInt32 size; |
| |
| ERR_CHECK(( (M4MP4C_FtypBox *)value)->major_brand != 0, |
| M4ERR_PARAMETER); |
| |
| /* Copy structure */ |
| mMp4FileDataPtr->ftyp = *(M4MP4C_FtypBox *)value; |
| |
| /* Update global position variables with the difference between common and |
| user block */ |
| size = |
| mMp4FileDataPtr->ftyp.nbCompatibleBrands * sizeof(M4OSA_UInt32); |
| |
| mMp4FileDataPtr->absoluteCurrentPos = 8/*mdat*/ + 16 + size; |
| mMp4FileDataPtr->filesize = 218/*mdat+moov+skip*/ + 16 + size; |
| } |
| break; |
| |
| case (M4MP4W_DSI): |
| { |
| streamIDmemAddrPtr = (M4SYS_StreamIDmemAddr *)value; |
| |
| /* Nested switch! Whee! */ |
| switch( streamIDmemAddrPtr->streamID ) |
| { |
| case (AudioStreamID): |
| return M4ERR_NOT_IMPLEMENTED; |
| |
| case (VideoStreamID): |
| |
| /* Protect DSI setting : only once allowed on a given stream */ |
| |
| switch( mMp4FileDataPtr->videoTrackPtr-> |
| CommonData.trackType ) |
| { |
| case M4SYS_kH263: |
| if ((0 != mMp4FileDataPtr->videoTrackPtr->dsiSize) |
| || (M4OSA_NULL |
| != mMp4FileDataPtr->videoTrackPtr->DSI)) |
| { |
| M4OSA_TRACE1_0( |
| "M4MP4W_setOption: dsi already set !"); |
| return M4ERR_STATE; |
| } |
| |
| if ((0 == streamIDmemAddrPtr->size) |
| || (M4OSA_NULL == streamIDmemAddrPtr->addr)) |
| { |
| M4OSA_TRACE1_0( |
| "M4MP4W_setOption: Bad H263 dsi!"); |
| return M4ERR_PARAMETER; |
| } |
| |
| /*decoder specific info size is supposed to be always 7 |
| bytes long */ |
| ERR_CHECK(streamIDmemAddrPtr->size == 7, |
| M4ERR_PARAMETER); |
| mMp4FileDataPtr->videoTrackPtr->dsiSize = |
| (M4OSA_UInt8)streamIDmemAddrPtr->size; |
| mMp4FileDataPtr->videoTrackPtr->DSI = (M4OSA_UChar |
| *)M4OSA_32bitAlignedMalloc(streamIDmemAddrPtr->size, |
| M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->DSI"); |
| ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->DSI |
| != M4OSA_NULL, M4ERR_ALLOC); |
| memcpy( |
| (void *)mMp4FileDataPtr->videoTrackPtr-> |
| DSI, |
| (void *)streamIDmemAddrPtr->addr, |
| streamIDmemAddrPtr->size); |
| |
| break; |
| |
| case M4SYS_kMPEG_4: |
| if ((0 != mMp4FileDataPtr->videoTrackPtr->dsiSize) |
| || (M4OSA_NULL |
| != mMp4FileDataPtr->videoTrackPtr->DSI)) |
| { |
| M4OSA_TRACE1_0( |
| "M4MP4W_setOption: dsi already set !"); |
| return M4ERR_STATE; |
| } |
| |
| if ((0 == streamIDmemAddrPtr->size) |
| || (M4OSA_NULL == streamIDmemAddrPtr->addr)) |
| { |
| M4OSA_TRACE1_0( |
| "M4MP4W_setOption: Bad MPEG4 dsi!"); |
| return M4ERR_PARAMETER; |
| } |
| |
| /*MP4V specific*/ |
| ERR_CHECK(streamIDmemAddrPtr->size < 105, |
| M4ERR_PARAMETER); |
| mMp4FileDataPtr->videoTrackPtr->dsiSize = |
| (M4OSA_UInt8)streamIDmemAddrPtr->size; |
| mMp4FileDataPtr->videoTrackPtr->DSI = (M4OSA_UChar |
| *)M4OSA_32bitAlignedMalloc(streamIDmemAddrPtr->size, |
| M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->DSI"); |
| ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->DSI |
| != M4OSA_NULL, M4ERR_ALLOC); |
| memcpy( |
| (void *)mMp4FileDataPtr->videoTrackPtr-> |
| DSI, |
| (void *)streamIDmemAddrPtr->addr, |
| streamIDmemAddrPtr->size); |
| mMp4FileDataPtr->filesize += |
| streamIDmemAddrPtr->size; |
| |
| break; |
| |
| case M4SYS_kH264: |
| if ((0 != mMp4FileDataPtr->videoTrackPtr->dsiSize) |
| || (M4OSA_NULL |
| != mMp4FileDataPtr->videoTrackPtr->DSI)) |
| { |
| /* + H.264 trimming */ |
| if (M4OSA_TRUE == mMp4FileDataPtr->bMULPPSSPS) |
| { |
| free(mMp4FileDataPtr->videoTrackPtr->DSI); |
| |
| // Do not strip the DSI |
| /* Store the DSI size */ |
| mMp4FileDataPtr->videoTrackPtr->dsiSize = |
| (M4OSA_UInt8)streamIDmemAddrPtr->size; |
| M4OSA_TRACE1_1("M4MP4W_setOption: in set option DSI size =%d"\ |
| ,mMp4FileDataPtr->videoTrackPtr->dsiSize); |
| /* Copy the DSI (SPS + PPS) */ |
| mMp4FileDataPtr->videoTrackPtr->DSI = |
| (M4OSA_UChar*)M4OSA_32bitAlignedMalloc( |
| streamIDmemAddrPtr->size, M4MP4_WRITER, |
| (M4OSA_Char *)"videoTrackPtr->DSI"); |
| ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->DSI != |
| M4OSA_NULL, M4ERR_ALLOC); |
| memcpy( |
| (void *)mMp4FileDataPtr->videoTrackPtr->DSI, |
| (void *)streamIDmemAddrPtr->addr, |
| streamIDmemAddrPtr->size); |
| |
| break; |
| /* - H.264 trimming */ |
| } |
| else |
| { |
| M4OSA_TRACE1_0( |
| "M4MP4W_setOption: dsi already set !"); |
| return M4ERR_STATE; |
| } |
| } |
| |
| if (( 0 == streamIDmemAddrPtr->size) |
| || (M4OSA_NULL == streamIDmemAddrPtr->addr)) |
| { |
| M4OSA_TRACE1_0( |
| "M4MP4W_setOption: Bad H264 dsi!"); |
| return M4ERR_PARAMETER; |
| } |
| |
| /* Store the DSI size */ |
| mMp4FileDataPtr->videoTrackPtr->dsiSize = |
| (M4OSA_UInt8)streamIDmemAddrPtr->size; |
| |
| /* Copy the DSI (SPS + PPS) */ |
| mMp4FileDataPtr->videoTrackPtr->DSI = (M4OSA_UChar |
| *)M4OSA_32bitAlignedMalloc(streamIDmemAddrPtr->size, |
| M4MP4_WRITER, (M4OSA_Char *)"videoTrackPtr->DSI"); |
| ERR_CHECK(mMp4FileDataPtr->videoTrackPtr->DSI |
| != M4OSA_NULL, M4ERR_ALLOC); |
| memcpy( |
| (void *)mMp4FileDataPtr->videoTrackPtr-> |
| DSI, |
| (void *)streamIDmemAddrPtr->addr, |
| streamIDmemAddrPtr->size); |
| break; |
| |
| default: |
| return M4ERR_BAD_STREAM_ID; |
| } |
| break; |
| |
| default: |
| return M4ERR_BAD_STREAM_ID; |
| } |
| } |
| break; |
| /* H.264 Trimming */ |
| case M4MP4W_MUL_PPS_SPS: |
| mMp4FileDataPtr->bMULPPSSPS = *(M4OSA_Int8 *)value; |
| /* H.264 Trimming */ |
| break; |
| |
| default: |
| return M4ERR_BAD_OPTION_ID; |
| } |
| |
| return err; |
| } |
| |
| /*******************************************************************************/ |
| M4OSA_ERR M4MP4W_getState( M4OSA_Context context, M4MP4W_State *state, |
| M4SYS_StreamID streamID ) |
| /*******************************************************************************/ |
| { |
| M4OSA_ERR err = M4NO_ERROR; |
| |
| M4MP4W_Mp4FileData *mMp4FileDataPtr = (M4MP4W_Mp4FileData *)context; |
| ERR_CHECK(context != M4OSA_NULL, M4ERR_PARAMETER); |
| |
| switch( streamID ) |
| { |
| case (0): |
| *state = mMp4FileDataPtr->state; |
| break; |
| |
| case (AudioStreamID): |
| if (mMp4FileDataPtr->hasAudio == M4OSA_TRUE) |
| { |
| *state = mMp4FileDataPtr->audioTrackPtr->microState; |
| } |
| else |
| { |
| return M4ERR_BAD_STREAM_ID; |
| } |
| break; |
| |
| case (VideoStreamID): |
| if (mMp4FileDataPtr->hasVideo == M4OSA_TRUE) |
| { |
| *state = mMp4FileDataPtr->videoTrackPtr->microState; |
| } |
| else |
| { |
| return M4ERR_BAD_STREAM_ID; |
| } |
| break; |
| |
| default: |
| return M4ERR_BAD_STREAM_ID; |
| } |
| |
| return err; |
| } |
| |
| /*******************************************************************************/ |
| M4OSA_ERR M4MP4W_getCurrentFileSize( M4OSA_Context context, |
| M4OSA_UInt32 *pCurrentFileSize ) |
| /*******************************************************************************/ |
| { |
| M4OSA_ERR err = M4NO_ERROR; |
| |
| M4MP4W_Mp4FileData *mMp4FileDataPtr = (M4MP4W_Mp4FileData *)context; |
| ERR_CHECK(context != M4OSA_NULL, M4ERR_PARAMETER); |
| |
| ERR_CHECK(pCurrentFileSize != M4OSA_NULL, M4ERR_PARAMETER); |
| *pCurrentFileSize = mMp4FileDataPtr->filesize; |
| |
| return err; |
| } |
| |
| #endif /* _M4MP4W_USE_CST_MEMORY_WRITER */ |