/* * 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 M4PCM_PCMReader.c * @brief PCM reader implementation * @note This file implements functions of the PCM reader ************************************************************************ */ #include "M4OSA_CharStar.h" #include "M4PCMR_CoreReader.h" #include "M4OSA_Debug.h" #include "M4OSA_CharStar.h" /** ****************************************************************************** * PCM reader version numbers ****************************************************************************** */ /* CHANGE_VERSION_HERE */ #define M4PCMR_VERSION_MAJOR 1 #define M4PCMR_VERSION_MINOR 0 #define M4PCMR_VERSION_REVISION 0 /** ************************************************************************ * M4OSA_ERR M4PCMR_openRead(M4OSA_Context* pContext, M4OSA_Void* pUrl, * M4OSA_FileReaderPointer* pFileFunction) * @brief This function opens a PCM file * @note This function : * - opens a PCM file * - initializes PCM context, * - verifies PCM file format * - Fill decoder config structure * - Changes state of the reader in 'Opening' * @param pContext: (OUT) Pointer on the PCM Reader context * @param pUrl: (IN) Name of the PCM file * @param pFileFunctions: (IN) Pointer on the file access functions * @return M4NO_ERROR there is no error during the opening * @return M4ERR_PARAMETER pContext and/or pUrl and/or pFileFunction is NULL * @return M4ERR_ALLOC there is no more memory available * @return M4ERR_FILE_NOT_FOUND the file cannot be found * @return M4PCMC_ERR_PCM_NOT_COMPLIANT the file does not seem to be compliant, no RIFF, * or lack of any mandatory chunk. * @return M4PCMC_ERR_PCM_NOT_SUPPORTED the PCM format of this file is not supported by the * reader * @return Any M4OSA_FILE errors see OSAL File specification for detailed errors ************************************************************************ */ M4OSA_ERR M4PCMR_openRead(M4OSA_Context* pContext, M4OSA_Void* pUrl, M4OSA_FileReadPointer* pFileFunction) { M4OSA_ERR err; M4PCMR_Context *context; M4OSA_Char* pTempURL; M4OSA_Char value[6]; /* Check parameters */ if((M4OSA_NULL == pContext)|| (M4OSA_NULL == pUrl) ||(M4OSA_NULL == pFileFunction)) { return M4ERR_PARAMETER; } /* Allocates the context */ context = M4OSA_NULL; context = (M4PCMR_Context *)M4OSA_32bitAlignedMalloc(sizeof(M4PCMR_Context), M4WAV_READER, (M4OSA_Char *)"M4PCMR_openRead"); if (M4OSA_NULL == context) { return M4ERR_ALLOC; } *pContext = (M4OSA_Context)context; /* Initialize the context */ context->m_offset = 0; context->m_state = M4PCMR_kInit; context->m_microState = M4PCMR_kInit; context->m_pFileReadFunc = M4OSA_NULL; context->m_fileContext = M4OSA_NULL; context->m_pAuBuffer = M4OSA_NULL; context->m_pDecoderSpecInfo = M4OSA_NULL; /* Set sample frequency */ pTempURL = (M4OSA_Char*)pUrl + (strlen((const char *)pUrl)-11); M4OSA_chrNCopy(value, pTempURL, 5); M4OSA_chrGetUInt32(pTempURL, &(context->m_decoderConfig.SampleFrequency), M4OSA_NULL, M4OSA_kchrDec); /* Set number of channels */ pTempURL += 6; M4OSA_chrNCopy(value, pTempURL, 1); M4OSA_chrGetUInt16(pTempURL, &(context->m_decoderConfig.nbChannels), M4OSA_NULL, M4OSA_kchrDec); M4OSA_chrNCopy(pUrl,pUrl, (strlen((const char *)pUrl)-12)); /* Open the file */ context->m_fileContext = M4OSA_NULL; err = pFileFunction->openRead(&(context->m_fileContext), pUrl, M4OSA_kFileRead); if(M4NO_ERROR != err) { return err; } context->m_decoderConfig.BitsPerSample = 16; context->m_decoderConfig.AvgBytesPerSec = context->m_decoderConfig.SampleFrequency * 2 \ * context->m_decoderConfig.nbChannels; err = pFileFunction->getOption(context->m_fileContext, M4OSA_kFileReadGetFileSize, (M4OSA_DataOption*)&(context->m_decoderConfig.DataLength)); if(M4NO_ERROR != err) { return err; } context->m_blockSize = 2048 * context->m_decoderConfig.nbChannels; // Raw PCM. Hence, get a // chunk of data if(context->m_decoderConfig.SampleFrequency == 8000) { /* AMR case, no pb */ context->m_blockSize = context->m_decoderConfig.nbChannels *\ (context->m_decoderConfig.SampleFrequency / 50) * \ (context->m_decoderConfig.BitsPerSample / 8); } if(context->m_decoderConfig.SampleFrequency == 16000) { /* AAC case, we can't read only 20 ms blocks */ context->m_blockSize = 2048 * context->m_decoderConfig.nbChannels; } context->m_dataStartOffset = 0; context->m_pFileReadFunc = pFileFunction; context->m_pAuBuffer = (M4OSA_MemAddr32)M4OSA_32bitAlignedMalloc(context->m_blockSize, M4WAV_READER, (M4OSA_Char *)"Core PCM reader Access Unit"); if (M4OSA_NULL == context->m_pAuBuffer) { err = M4ERR_ALLOC; goto cleanup; } /* Change state */ context->m_state = M4PCMR_kOpening; return M4NO_ERROR; cleanup: /* Close the file */ if(context->m_pFileReadFunc != M4OSA_NULL) context->m_pFileReadFunc->closeRead(context->m_fileContext); /* Free internal context */ free(context); *pContext = M4OSA_NULL; return err; } /** ************************************************************************ * M4OSA_ERR M4PCMR_getNextStream(M4OSA_Context context, M4SYS_StreamDescription* pStreamDesc) * @brief This function get the (unique) stream of a PCM file * @note This function : * - Allocates and fills the decoder specific info structure * - Fills decoder specific infos structure * - Fills pStreamDesc structure allocated by the caller * @param context: (IN/OUT) PCM Reader context * @param pStreamDesc: (IN) Stream Description context * @return M4NO_ERROR there is no error * @return M4ERR_PARAMETER at least one parameter is NULL * @return M4ERR_ALLOC there is no more memory available * @return M4ERR_STATE this function cannot be called now * @return Any M4OSA_FILE errors see OSAL File specification for detailed errors ************************************************************************ */ M4OSA_ERR M4PCMR_getNextStream(M4OSA_Context context, M4SYS_StreamDescription* pStreamDesc) { M4PCMR_Context *c = (M4PCMR_Context *)context; /* Check parameters */ if((M4OSA_NULL == context)|| (M4OSA_NULL == pStreamDesc)) { return M4ERR_PARAMETER; } if (c->m_state == M4PCMR_kOpening_streamRetrieved) { return M4WAR_NO_MORE_STREAM; } /* Check Reader's m_state */ if(c->m_state != M4PCMR_kOpening) { return M4ERR_STATE; } /* Only one stream is contained in PCM file */ pStreamDesc->streamID = 1; /* Not used */ pStreamDesc->profileLevel = 0; pStreamDesc->decoderSpecificInfoSize = sizeof(M4PCMC_DecoderSpecificInfo); /* Allocates decoder specific info structure */ pStreamDesc->decoderSpecificInfo = M4OSA_NULL; pStreamDesc->decoderSpecificInfo = (M4OSA_MemAddr32)M4OSA_32bitAlignedMalloc( sizeof(M4PCMC_DecoderSpecificInfo), M4WAV_READER, (M4OSA_Char *)"M4PCMR_getNextStream"); if(pStreamDesc->decoderSpecificInfo == M4OSA_NULL) { return M4ERR_ALLOC; } /* Fill decoderSpecificInfo structure, with decoder config structure filled in 'openread' function */ memcpy((void *)pStreamDesc->decoderSpecificInfo, (void *)&c->m_decoderConfig, sizeof(M4PCMC_DecoderSpecificInfo)); /* Fill other fields of pStreamDesc structure */ pStreamDesc->timeScale = 1000; pStreamDesc->duration = (M4OSA_Time)(((M4OSA_Double)(c->m_decoderConfig.DataLength)\ / (M4OSA_Double)(c->m_decoderConfig.AvgBytesPerSec))*pStreamDesc->timeScale); pStreamDesc->averageBitrate = c->m_decoderConfig.AvgBytesPerSec * 8;/* in bits, multiply by 8*/ pStreamDesc->maxBitrate = pStreamDesc->averageBitrate; /* PCM stream has constant bitrate */ /* Determines Stream type */ switch(c->m_decoderConfig.BitsPerSample) { case 8: switch(c->m_decoderConfig.nbChannels) { case 1: pStreamDesc->streamType = M4SYS_kPCM_8bitsU; break; // case 2: // pStreamDesc->streamType = M4SYS_kPCM_8bitsS; /* ??? 8bits stereo not // defined ? */ // break; default: pStreamDesc->streamType = M4SYS_kAudioUnknown; } break; case 16: switch(c->m_decoderConfig.nbChannels) { case 1: pStreamDesc->streamType = M4SYS_kPCM_16bitsU; break; case 2: pStreamDesc->streamType = M4SYS_kPCM_16bitsS; break; default: pStreamDesc->streamType = M4SYS_kAudioUnknown; } break; default: pStreamDesc->streamType = M4SYS_kAudioUnknown; } c->m_pDecoderSpecInfo = pStreamDesc->decoderSpecificInfo; c->m_state = M4PCMR_kOpening_streamRetrieved; return M4NO_ERROR; } /** ************************************************************************ * M4OSA_ERR M4PCMR_startReading(M4OSA_Context context, M4SYS_StreamID* pStreamIDs) * @brief This function starts reading the unique stream of a PCM file * @note This function : * - Verifies that the current reader's state allows to start reading a stream * - Check that provided StreamId is correct (always true, only one stream...) * In the player application, a StreamId table is initialized as follow: * M4SYS_StreamID pStreamID[2]={1,0}; * - Change state of the reader in 'Reading' * @param context: (IN/OUT) PCM Reader context * @param streamID: (IN) Stream selection * @return M4NO_ERROR there is no error * @return M4ERR_PARAMETER at least one parameter is NULL * @return M4ERR_STATE this function cannot be called now * @return M4ERR_BAD_STREAM_ID at least one of the streamID does not exist * (should never happen if table pStreamID is correctly initialized as above) ************************************************************************ */ M4OSA_ERR M4PCMR_startReading(M4OSA_Context context, M4SYS_StreamID* pStreamIDs) { M4PCMR_Context *c = (M4PCMR_Context *)context; /* Check parameters */ if((M4OSA_NULL == context) || (M4OSA_NULL == pStreamIDs)) { return M4ERR_PARAMETER; } /* Check Reader's state */ if(c->m_state != M4PCMR_kOpening_streamRetrieved) { return M4ERR_STATE; } /* Check pStreamID and if they're OK, change reader's state */ if(pStreamIDs[0] == 1 || pStreamIDs[0] == 0) /* First and unique stream contained in PCM file */ { c->m_state = M4PCMR_kReading; c->m_microState = M4PCMR_kReading; } else { return M4ERR_BAD_STREAM_ID; } return M4NO_ERROR; } /** ************************************************************************ * M4OSA_ERR M4PCMR_nextAU(M4OSA_Context context, M4SYS_StreamID streamID, M4SYS_AccessUnit* pAU) * @brief This function reads the next AU contained in the PCM file * @note This function : * - Verifies that the current reader's state allows to read an AU * - Allocates memory to store read AU * - Read data from file and store them into previously allocated memory * - Fill AU structure fileds (CTS...) * - Change state of the reader in 'Reading' (not useful...) * - Change Micro state 'Reading' in M4PCMR_kReading_nextAU * (AU is read and can be deleted) * - Check if the last AU has been read or if we're about to read it * @param context: (IN/OUT) PCM Reader context * @param streamID: (IN) Stream selection * @param pAU: (IN/OUT) Acces Unit Structure * @return M4NO_ERROR there is no error * @return M4ERR_PARAMETER at least one parameter is NULL * @return M4ERR_ALLOC there is no more memory available * @return M4ERR_STATE this function cannot be called now * @return M4M4WAR_NO_DATA_YET there is no enough data in the file to provide a new access unit. * @return M4WAR_END_OF_STREAM There is no more access unit in the stream, * or the sample number is bigger the maximum one. ************************************************************************ */ M4OSA_ERR M4PCMR_nextAU(M4OSA_Context context, M4SYS_StreamID streamID, M4SYS_AccessUnit* pAU) { M4PCMR_Context *c = (M4PCMR_Context *)context; M4OSA_ERR err = M4NO_ERROR; M4OSA_UInt32 size_read; /* Check parameters */ if((M4OSA_NULL == context) || (M4OSA_NULL == pAU)) { return M4ERR_PARAMETER; } /* Check Reader's state */ if(c->m_state != M4PCMR_kReading && c->m_microState != M4PCMR_kReading) { return M4ERR_STATE; } /* Allocates AU dataAdress */ pAU->dataAddress = c->m_pAuBuffer; size_read = c->m_blockSize; if((c->m_offset + size_read) >= c->m_decoderConfig.DataLength) { size_read = c->m_decoderConfig.DataLength - c->m_offset; } /* Read data in file, and copy it to AU Structure */ err = c->m_pFileReadFunc->readData(c->m_fileContext, (M4OSA_MemAddr8)pAU->dataAddress, (M4OSA_UInt32 *)&size_read); if(M4NO_ERROR != err) { return err; } /* Calculates the new m_offset, used to determine whether we're at end of reading or not */ c->m_offset = c->m_offset + size_read; /* Fill others parameters of AU structure */ pAU->CTS = (M4OSA_Time)(((M4OSA_Double)c->m_offset/(M4OSA_Double)c->m_decoderConfig.AvgBytesPerSec)\ *1000); pAU->DTS = pAU->CTS; pAU->attribute = 0; pAU->frag = M4OSA_NULL; pAU->nbFrag = 0; pAU->stream = M4OSA_NULL; pAU->size = size_read; /* Change states */ c->m_state = M4PCMR_kReading; /* Not changed ... */ c->m_microState = M4PCMR_kReading_nextAU; /* AU is read and can be deleted */ /* Check if there is another AU to read */ /* ie: if decoded nb of bytes = nb of bytes to decode, it means there is no more AU to decode */ if(c->m_offset >= c->m_decoderConfig.DataLength) { return M4WAR_NO_MORE_AU; } return M4NO_ERROR; } /** ************************************************************************ * M4OSA_ERR M4PCMR_freeAU(M4OSA_Context context, M4SYS_StreamID streamID, M4SYS_AccessUnit* pAU) * @brief This function frees the AU provided in parameter * @note This function : * - Verifies that the current reader's state allows to free an AU * - Free dataAddress field of AU structure * - Change state of the reader in 'Reading' (not useful...) * - Change Micro state 'Reading' in M4PCMR_kReading (another AU can be read) * @param context: (IN/OUT) PCM Reader context * @param streamID: (IN) Stream selection * @param pAU: (IN) Acces Unit Structure * @return M4NO_ERROR there is no error * @return M4ERR_PARAMETER at least one parameter is NULL * @return M4ERR_STATE this function cannot be called now ************************************************************************ */ M4OSA_ERR M4PCMR_freeAU(M4OSA_Context context, M4SYS_StreamID streamID, M4SYS_AccessUnit* pAU) { M4PCMR_Context *c = (M4PCMR_Context *)context; /* Check parameters */ if((M4OSA_NULL == context ) || (M4OSA_NULL == pAU)) { return M4ERR_PARAMETER; } /* Check Reader's state */ if(c->m_state != M4PCMR_kReading && c->m_microState != M4PCMR_kReading_nextAU) { return M4ERR_STATE; } pAU->dataAddress = M4OSA_NULL; /* Change states */ c->m_state = M4PCMR_kReading; /* Not changed ... */ c->m_microState = M4PCMR_kReading; /* AU is deleted, another AU can be read */ return M4NO_ERROR; } /** ************************************************************************ * M4OSA_ERR M4PCMR_seek(M4OSA_Context context, M4SYS_StreamID* pStreamID, M4OSA_Time time, M4SYS_seekAccessMode seekAccessMode, M4OSA_Time* pObtainCTS[]) * @brief This function seeks into the PCM file at the provided time * @note This function : * - Verifies that the current reader's state allows to seek * - Determines from provided time m_offset to seek in file * - If m_offset is correct, seek in file * - Update new m_offset in PCM reader context * @param context: (IN/OUT) PCM Reader context * @param pStreamID: (IN) Stream selection (not used, only 1 stream) * @param time: (IN) Targeted time * @param seekMode: (IN) Selects the seek access mode * @param pObtainCTS[]: (OUT) Returned Time (not used) * @return M4NO_ERROR there is no error * @return M4ERR_PARAMETER at least one parameter is NULL * @return M4ERR_ALLOC there is no more memory available * @return M4ERR_STATE this function cannot be called now * @return M4WAR_INVALID_TIME Specified time is not reachable * @param M4ERR_NOT_IMPLEMENTED This seek mode is not implemented yet ************************************************************************ */ M4OSA_ERR M4PCMR_seek(M4OSA_Context context, M4SYS_StreamID* pStreamID, M4OSA_Time time, M4SYS_SeekAccessMode seekAccessMode, M4OSA_Time* pObtainCTS) { M4PCMR_Context *c = (M4PCMR_Context *)context; M4OSA_ERR err = M4NO_ERROR; M4OSA_UInt32 offset; M4OSA_UInt32 alignment; M4OSA_UInt32 size_read; /* Check parameters */ if((M4OSA_NULL == context) || (M4OSA_NULL == pStreamID)) { return M4ERR_PARAMETER; } /* Check Reader's state */ if(c->m_state != M4PCMR_kOpening_streamRetrieved && c->m_state != M4PCMR_kReading) { return M4ERR_STATE; } switch(seekAccessMode) { case M4SYS_kBeginning: /* Determine m_offset from time*/ offset = (M4OSA_UInt32)(time * ((M4OSA_Double)(c->m_decoderConfig.AvgBytesPerSec) / 1000)); /** check the alignment on sample boundary */ alignment = c->m_decoderConfig.nbChannels*c->m_decoderConfig.BitsPerSample/8; if (offset%alignment != 0) { offset -= offset%alignment; } /*add the header offset*/ offset += c->m_dataStartOffset; /* If m_offset is over file size -> Invalid time */ if (offset > (c->m_dataStartOffset + c->m_decoderConfig.DataLength)) { return M4WAR_INVALID_TIME; } else { /* Seek file */ size_read = offset; err = c->m_pFileReadFunc->seek(c->m_fileContext, M4OSA_kFileSeekBeginning, (M4OSA_FilePosition *) &size_read); if(M4NO_ERROR != err) { return err; } /* Update m_offset in M4PCMR_context */ c->m_offset = offset - c->m_dataStartOffset; } break; default: return M4ERR_NOT_IMPLEMENTED; } return M4NO_ERROR; } /** ************************************************************************ * M4OSA_ERR M4PCMR_closeRead(M4OSA_Context context) * @brief This function closes PCM file, and frees context * @note This function : * - Verifies that the current reader's state allows close the PCM file * - Closes the file * - Free structures * @param context: (IN/OUT) PCM Reader context * @return M4NO_ERROR there is no error * @return M4ERR_PARAMETER at least one parameter is NULL * @return M4ERR_STATE this function cannot be called now ************************************************************************ */ M4OSA_ERR M4PCMR_closeRead(M4OSA_Context context) { M4PCMR_Context *c = (M4PCMR_Context *)context; M4OSA_ERR err = M4NO_ERROR; /* Check parameters */ if(M4OSA_NULL == context) { return M4ERR_PARAMETER; } if(c->m_pDecoderSpecInfo != M4OSA_NULL) { free(c->m_pDecoderSpecInfo); } /* Check Reader's state */ if(c->m_state != M4PCMR_kReading) { return M4ERR_STATE; } else if(c->m_microState == M4PCMR_kReading_nextAU) { return M4ERR_STATE; } if (M4OSA_NULL != c->m_pAuBuffer) { free(c->m_pAuBuffer); } /* Close the file */ if (M4OSA_NULL != c->m_pFileReadFunc) { err = c->m_pFileReadFunc->closeRead(c->m_fileContext); } /* Free internal context */ if (M4OSA_NULL != c) { free(c); } return err; } /** ************************************************************************ * M4OSA_ERR M4PCMR_getOption(M4OSA_Context context, M4PCMR_OptionID optionID, * M4OSA_DataOption* pValue) * @brief This function get option of the PCM Reader * @note This function : * - Verifies that the current reader's state allows to get an option * - Return corresponding option value * @param context: (IN/OUT) PCM Reader context * @param optionID: (IN) ID of the option to get * @param pValue: (OUT) Variable where the option value is returned * @return M4NO_ERROR there is no error. * @return M4ERR_PARAMETER at least one parameter is NULL. * @return M4ERR_BAD_OPTION_ID the optionID is not a valid one. * @return M4ERR_STATE this option is not available now. * @return M4ERR_NOT_IMPLEMENTED this option is not implemented ************************************************************************ */ M4OSA_ERR M4PCMR_getOption(M4OSA_Context context, M4PCMR_OptionID optionID, M4OSA_DataOption* pValue) { M4PCMR_Context *c =(M4PCMR_Context *)context; /* Check parameters */ if(M4OSA_NULL == context) { return M4ERR_PARAMETER; } /* Check reader's state */ if((c->m_state != M4PCMR_kOpening) && (c->m_state != M4PCMR_kOpening_streamRetrieved)\ && (c->m_state != M4PCMR_kReading)) { return M4ERR_STATE; } /* Depend of the OptionID, the value to return is different */ switch(optionID) { case M4PCMR_kPCMblockSize: *pValue = &c->m_blockSize; break; default: return M4ERR_BAD_OPTION_ID; } return M4NO_ERROR; } /** ************************************************************************ * M4OSA_ERR M4PCMR_setOption(M4OSA_Context context, M4PCMR_OptionID optionID, * M4OSA_DataOption Value) * @brief This function set option of the PCM Reader * @note This function : * - Verifies that the current reader's state allows to set an option * - Set corresponding option value * @param context: (IN/OUT) PCM Reader context * @param optionID: (IN) ID of the option to get * @param Value: (IN) Variable where the option value is stored * @return M4NO_ERROR there is no error. * @return M4ERR_PARAMETER at least one parameter is NULL. * @return M4ERR_BAD_OPTION_ID the optionID is not a valid one. * @return M4ERR_STATE this option is not available now. * @return M4ERR_NOT_IMPLEMENTED this option is not implemented ************************************************************************ */ M4OSA_ERR M4PCMR_setOption(M4OSA_Context context, M4PCMR_OptionID optionID, M4OSA_DataOption Value) { M4PCMR_Context *c =(M4PCMR_Context *)context; /* Check parameters */ if(context == M4OSA_NULL) { return M4ERR_PARAMETER; } /* Check reader's state */ if((c->m_state != M4PCMR_kOpening) && (c->m_state != M4PCMR_kOpening_streamRetrieved)\ && (c->m_state != M4PCMR_kReading)) { return M4ERR_STATE; } /* Depend of the OptionID, the value to set is different */ switch(optionID) { case M4PCMR_kPCMblockSize: c->m_blockSize = (M4OSA_UInt32)(uintptr_t)Value; break; default: return M4ERR_BAD_OPTION_ID; } return M4NO_ERROR; } /*********************************************************/ M4OSA_ERR M4PCMR_getVersion (M4_VersionInfo *pVersion) /*********************************************************/ { M4OSA_TRACE1_1("M4PCMR_getVersion called with pVersion: 0x%x", pVersion); M4OSA_DEBUG_IF1(((M4OSA_UInt32) pVersion == 0),M4ERR_PARAMETER, "pVersion is NULL in M4PCMR_getVersion"); pVersion->m_major = M4PCMR_VERSION_MAJOR; pVersion->m_minor = M4PCMR_VERSION_MINOR; pVersion->m_revision = M4PCMR_VERSION_REVISION; return M4NO_ERROR; }