blob: a20fe40100c4354120e17c27384e12f61ef25d50 [file] [log] [blame]
/* ------------------------------------------------------------------
* Copyright (C) 2008 PacketVideo
*
* 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.
* -------------------------------------------------------------------
*/
#include "h264_media_info_parser.h"
#include "oscl_string_utils.h"
#include "oscl_string_containers.h"
#include "sdp_parsing_utils.h"
#define MIN_ENCODED_BYTES 4
#define AVC_NALTYPE_MASK 0x1F
#define AVC_NALTYPE_SPS 7
#define AVC_NALTYPE_PPS 8
SDP_ERROR_CODE
SDPH264MediaInfoParser::parseMediaInfo(const char *buff,
const int index,
SDPInfo *sdp,
payloadVector payload_vec,
bool isSipSdp,
int alt_id,
bool alt_def_id)
{
//Pointer to the beginning of the media text
const char *current_start = buff;
//Pointer to the end of the media text
const char *end = buff + index;
const char *line_start_ptr, *line_end_ptr;
int fmtp_cnt = 0;
bool altMedia = false;
if (!alt_id || (alt_def_id == true))
{
altMedia = false;
}
else
{
altMedia = true;
}
void *memory = sdp->alloc(sizeof(h264_mediaInfo), altMedia);
if (NULL == memory)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Memory allocation failure"));
return SDP_NO_MEMORY;
}
else
{
h264_mediaInfo *h264V = OSCL_PLACEMENT_NEW(memory, h264_mediaInfo());
h264V->setMediaInfoID(sdp->getMediaObjectIndex());
// Allocate memory to the payload specific objects
for (uint32 ii = 0; ii < payload_vec.size(); ii++)
{
void* mem = h264V->alloc(sizeof(H264PayloadSpecificInfoType));
if (mem == NULL)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Memory allocation failure"));
return SDP_NO_MEMORY;
}
else
{
H264PayloadSpecificInfoType* h264Payload = OSCL_PLACEMENT_NEW(mem, H264PayloadSpecificInfoType(payload_vec[ii]));
(void) h264Payload;
}
}
if (alt_id && !alt_def_id)
{
sdp->copyFmDefMedia(h264V);
//empty alternate & default track ID vectors.
h264V->resetAlternateTrackId();
h264V->resetDependentTrackId();
}
SDP_ERROR_CODE status =
baseMediaInfoParser(buff, h264V, index, alt_id, alt_def_id, isSipSdp);
if (status != SDP_SUCCESS)
{
return status;
}
while (get_next_line(current_start,
end,
line_start_ptr,
line_end_ptr))
{
switch (*line_start_ptr)
{
case 'a':
{
if ((!oscl_strncmp(line_start_ptr,
"a=alt:",
oscl_strlen("a=alt:"))) &&
(alt_def_id == false))
{
line_start_ptr += oscl_strlen("a=alt:");
for (; *line_start_ptr != ':'; line_start_ptr++);
line_start_ptr = line_start_ptr + 1;
}
if (!oscl_strncmp(line_start_ptr,
"a=fmtp:",
oscl_strlen("a=fmtp:")))
{
const char *tmp_start_line, *tmp_end_line;
fmtp_cnt++ ;
tmp_start_line = line_start_ptr + oscl_strlen("a=fmtp:");
tmp_start_line =
skip_whitespace(tmp_start_line, line_end_ptr);
if (tmp_start_line >= line_end_ptr)
{
break;
}
tmp_end_line =
skip_to_whitespace(tmp_start_line, line_end_ptr);
if (tmp_end_line < tmp_start_line)
{
break;
}
uint32 payloadNumber;
if (PV_atoi(tmp_start_line,
'd',
(tmp_end_line - tmp_start_line),
payloadNumber) == false)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format - Bad payload number"));
return SDP_BAD_MEDIA_FMTP;
}
else
{
int p;
if (!h264V->lookupPayloadNumber(payloadNumber, p))
{
fmtp_cnt--;
break;
}
}
// payloadNumber is present in the mediaInfo. get the payload
// Specific pointer corresponding to this payload
H264PayloadSpecificInfoType* payloadPtr =
(H264PayloadSpecificInfoType*)h264V->getPayloadSpecificInfoTypePtr(payloadNumber);
if (payloadPtr == NULL)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: Payload pointer not found for payload"));
return SDP_PAYLOAD_MISMATCH;
}
PVMF_SDP_PARSER_LOGINFO((0, "SDPH264MediaInfoParser::parseMediaInfo - processing payload number : %d", payloadNumber));
tmp_start_line = tmp_end_line + 1;
tmp_start_line =
skip_whitespace(tmp_start_line, line_end_ptr);
if (tmp_start_line >= line_end_ptr)
{
break;
}
int ii = 0;
const char *temp = tmp_start_line;
for (ii = 0; ii < (line_end_ptr - tmp_start_line); ii++)
{
if ((tmp_start_line[ii] == ';') ||
(ii == (line_end_ptr - tmp_start_line) - 1))
{
tmp_end_line = tmp_start_line + ii;
if (ii == (line_end_ptr - tmp_start_line) - 1)
{
tmp_end_line += 1;
}
if (!oscl_strncmp(temp,
"profile-level-id=",
oscl_strlen("profile-level-id=")))
{
temp += oscl_strlen("profile-level-id=");
temp = skip_whitespace(temp, tmp_end_line);
if (temp > tmp_end_line)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format - Bad profile-level-id: field"));
return SDP_BAD_MEDIA_FMTP;
}
uint32 val;
if (PV_atoi(temp,
'x',
(tmp_end_line - temp),
val) == true)
{
payloadPtr->setProfileLevelID(val);
}
}
if (!oscl_strncmp(temp,
"max-mbps=",
oscl_strlen("max-mbps=")))
{
temp += oscl_strlen("max-mbps=");
temp = skip_whitespace(temp, tmp_end_line);
if (temp > tmp_end_line)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format - Bad max-mbps: field"));
return SDP_BAD_MEDIA_FMTP;
}
uint32 val;
if (PV_atoi(temp,
'd',
(tmp_end_line - temp),
val) == true)
{
payloadPtr->setMaxMbps(val);
}
}
if (!oscl_strncmp(temp,
"max-fs=",
oscl_strlen("max-fs=")))
{
temp += oscl_strlen("max-fs=");
temp = skip_whitespace(temp, tmp_end_line);
if (temp > tmp_end_line)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format - Bad max-fs field"));
return SDP_BAD_MEDIA_FMTP;
}
uint32 val;
if (PV_atoi(temp,
'd',
(tmp_end_line - temp),
val) == true)
{
payloadPtr->setMaxFs(val);
}
}
if (!oscl_strncmp(temp,
"max-cpb=",
oscl_strlen("max-cpb=")))
{
temp += oscl_strlen("max-cpb=");
temp = skip_whitespace(temp, tmp_end_line);
if (temp > tmp_end_line)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format - Bad max-cpb: field"));
return SDP_BAD_MEDIA_FMTP;
}
uint32 val;
if (PV_atoi(temp,
'd',
(tmp_end_line - temp),
val) == true)
{
payloadPtr->setMaxCpb(val);
}
}
if (!oscl_strncmp(temp,
"max-dpb=",
oscl_strlen("max-dpb=")))
{
temp += oscl_strlen("max-dpb=");
temp = skip_whitespace(temp, tmp_end_line);
if (temp > tmp_end_line)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format - Bad max-dpb field"));
return SDP_BAD_MEDIA_FMTP;
}
uint32 val;
if (PV_atoi(temp,
'd',
(tmp_end_line - temp),
val) == true)
{
payloadPtr->setMaxDpb(val);
}
}
if (!oscl_strncmp(temp,
"max-br=",
oscl_strlen("max-br=")))
{
temp += oscl_strlen("max-br=");
temp = skip_whitespace(temp, tmp_end_line);
if (temp > tmp_end_line)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format - Bad max-br field"));
return SDP_BAD_MEDIA_FMTP;
}
uint32 val;
if (PV_atoi(temp,
'd',
(tmp_end_line - temp),
val) == true)
{
payloadPtr->setMaxBr(val);
}
}
if (!oscl_strncmp(temp,
"redundant-pic-cap=",
oscl_strlen("redundant-pic-cap=")))
{
temp += oscl_strlen("redundant-pic-cap=");
temp = skip_whitespace(temp, tmp_end_line);
if (temp > tmp_end_line)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format - Bad redundant-pic-cap field"));
return SDP_BAD_MEDIA_FMTP;
}
uint32 val;
if (PV_atoi(temp,
'd',
(tmp_end_line - temp),
val) == true)
{
payloadPtr->setRedundantPicCap(val);
}
}
if (!oscl_strncmp(temp,
"sprop-parameter-sets=",
oscl_strlen("sprop-parameter-sets=")))
{
temp += oscl_strlen("sprop-parameter-sets=");
temp = skip_whitespace(temp, line_end_ptr);
if (temp >= line_end_ptr)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format - Bad sprop-parameter-sets field"));
return SDP_BAD_MEDIA_FMTP;
}
uint32 parameterSetLength = (int)(tmp_end_line - temp);
#if 0
SDPAllocDestructDealloc<uint8> SDP_alloc;
uint8 *mptr = SDP_alloc.ALLOCATE(parameterSetLength);
OsclRefCounterSA< SDPAllocDestructDealloc<uint8> > *refcnt =
new OsclRefCounterSA< SDPAllocDestructDealloc<uint8> >(mptr);
OsclSharedPtr<uint8> parameterSetPtr(mptr, refcnt);
uint8* inBuf = (uint8*)(temp);
uint32 outBufLen = 0;
if (!sdp_decodebase64(inBuf,
parameterSetLength,
mptr,
outBufLen,
parameterSetLength))
{
return SDP_BAD_MEDIA_FORMAT;
}
if (!h264V->setDecoderSpecificInfo(parameterSetPtr, payloadNumber))
return SDP_PAYLOAD_MISMATCH;
if (!h264V->setDecoderSpecificInfoSize(parameterSetLength, payloadNumber))
return SDP_PAYLOAD_MISMATCH;
#else
SDP_ERROR_CODE errCode =
parseParameterSets(temp,
parameterSetLength,
h264V,
payloadNumber);
if (errCode != SDP_SUCCESS)
{
return errCode;
}
#endif
}
if (!oscl_strncmp(temp,
"packetization-mode=",
oscl_strlen("packetization-mode=")))
{
temp += oscl_strlen("packetization-mode=");
temp = skip_whitespace(temp, tmp_end_line);
if (temp > tmp_end_line)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format - Bad packetization-mode field"));
return SDP_BAD_MEDIA_FMTP;
}
uint32 val;
if (PV_atoi(temp,
'd',
(tmp_end_line - temp),
val) == true)
{
payloadPtr->setPacketizationMode(val);
}
}
if (!oscl_strncmp(temp,
"sprop-interleaving-depth=",
oscl_strlen("sprop-interleaving-depth=")))
{
temp += oscl_strlen("sprop-interleaving-depth=");
temp = skip_whitespace(temp, tmp_end_line);
if (temp > tmp_end_line)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format - Bad sprop-interleaving-depth field"));
return SDP_BAD_MEDIA_FMTP;
}
uint32 val;
if (PV_atoi(temp,
'd',
(tmp_end_line - temp),
val) == true)
{
payloadPtr->setSpropInterleavingDepth(val);
}
}
if (!oscl_strncmp(temp,
"deint-buf-cap=",
oscl_strlen("deint-buf-cap=")))
{
temp += oscl_strlen("deint-buf-cap=");
temp = skip_whitespace(temp, tmp_end_line);
if (temp > tmp_end_line)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format - Bad deint-buf-cap field"));
return SDP_BAD_MEDIA_FMTP;
}
uint32 val;
if (PV_atoi(temp,
'd',
(tmp_end_line - temp),
val) == true)
{
payloadPtr->setDeintBufCap(val);
}
}
if (!oscl_strncmp(temp,
"sprop-deint-buf-req=",
oscl_strlen("sprop-deint-buf-req=")))
{
temp += oscl_strlen("sprop-deint-buf-req=");
temp = skip_whitespace(temp, tmp_end_line);
if (temp > tmp_end_line)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format - Bad sprop-deint-buf-req field"));
return SDP_BAD_MEDIA_FMTP;
}
uint32 val;
if (PV_atoi(temp,
'd',
(tmp_end_line - temp),
val) == true)
{
payloadPtr->setSpropDeintBufReq(val);
}
}
if (!oscl_strncmp(temp,
"sprop-init-buf-time=",
oscl_strlen("sprop-init-buf-time=")))
{
temp += oscl_strlen("sprop-init-buf-time=");
temp = skip_whitespace(temp, tmp_end_line);
if (temp > tmp_end_line)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format - Bad sprop-init-buf-time field"));
return SDP_BAD_MEDIA_FMTP;
}
uint32 val;
if (PV_atoi(temp,
'd',
(tmp_end_line - temp),
val) == true)
{
payloadPtr->setSpropInitBufTime(val);
}
}
if (!oscl_strncmp(temp,
"sprop-max-don-diff=",
oscl_strlen("sprop-max-don-diff=")))
{
temp += oscl_strlen("sprop-max-don-diff=");
temp = skip_whitespace(temp, tmp_end_line);
if (temp > tmp_end_line)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format - Bad sprop-max-don-diff field"));
return SDP_BAD_MEDIA_FMTP;
}
uint32 val;
if (PV_atoi(temp,
'd',
(tmp_end_line - temp),
val) == true)
{
payloadPtr->setSpropMaxDonDiff(val);
}
}
if (!oscl_strncmp(temp,
"max-rcmd-nalu-size=",
oscl_strlen("max-rcmd-nalu-size=")))
{
temp += oscl_strlen("max-rcmd-nalu-size=");
temp = skip_whitespace(temp, tmp_end_line);
if (temp > tmp_end_line)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format - Bad max-rcmd-nalu-size field"));
return SDP_BAD_MEDIA_FMTP;
}
uint32 val;
if (PV_atoi(temp,
'd',
(tmp_end_line - temp),
val) == true)
{
payloadPtr->setMaxRcmdNaluSize(val);
}
}
if (!oscl_strncmp(temp,
"decode_buf=",
oscl_strlen("decode_buf=")))
{
temp += oscl_strlen("decode_buf=");
temp = skip_whitespace(temp, tmp_end_line);
if (temp > tmp_end_line)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format - Bad decode-buf field"));
return SDP_BAD_MEDIA_FMTP;
}
uint32 decode_buf;
if (PV_atoi(temp,
'd',
(tmp_end_line - temp),
decode_buf) == true)
{
payloadPtr->setMaxBufferSize(decode_buf);
}
}
if (tmp_end_line != line_end_ptr)
{
temp = tmp_end_line + 1;
}
temp = skip_whitespace(temp, line_end_ptr);
if (temp >= line_end_ptr)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - Bad a=fmtp: line format"));
return SDP_BAD_MEDIA_FMTP;
}
}
}
}// end a=fmtp
}
break;
default:
{
}
break;
}
current_start = line_end_ptr;
}
sessionDescription *session = sdp->getSessionInfo();
const char *altGroupBW = session->getAltGroupBW();
int length = session->getAltGroupBWLength();
if (length > 0)
{
status = setDependentMediaId(altGroupBW, length, h264V, alt_id);
if (status != SDP_SUCCESS)
return SDP_BAD_MEDIA_ALT_ID;
}
const char *altGroupLANG = session->getAltGroupLANG();
length = session->getAltGroupLANGLength();
if (length > 0)
{
status = setDependentMediaId(altGroupLANG, length, h264V, alt_id);
if (status != SDP_SUCCESS)
return SDP_BAD_MEDIA_ALT_ID;
}
if ((fmtp_cnt == h264V->getMediaPayloadNumberCount()) &&
(h264V->getCFieldStatus() || session->getCFieldStatus()))
return SDP_SUCCESS;
else
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseMediaInfo - no c field present"));
return SDP_FAILURE_NO_C_FIELD;
}
}
}
SDP_ERROR_CODE SDPH264MediaInfoParser::parseParameterSets(const char* aParamSetBuf,
int aParamSetBufLen,
h264_mediaInfo* aH264MediaInfo,
uint32 aPayLoadNumber)
{
/*Determine number of parameter sets */
const char *tmp_start_line = aParamSetBuf;
int i;
uint32 numParamSets = 0;
Oscl_Vector<uint8*, OsclMemAllocator> paramSetStartBufVec;
Oscl_Vector<uint32, OsclMemAllocator> paramSetBufSizeVec;
paramSetStartBufVec.push_back((uint8*)tmp_start_line);
uint32 prevIndex = 0;
for (i = 0; i < aParamSetBufLen; i++)
{
if (tmp_start_line[i] == ',')
{
uint8* ptr = (uint8*)(tmp_start_line + (i + 1));
paramSetStartBufVec.push_back(ptr);
uint32 size = (i - prevIndex);
prevIndex = i + 1;
paramSetBufSizeVec.push_back(size);
numParamSets++;
}
}
numParamSets++;
paramSetBufSizeVec.push_back((aParamSetBufLen - prevIndex));
if (paramSetBufSizeVec[0] >= MIN_ENCODED_BYTES)
{
uint8 decoded[MIN_ENCODED_BYTES];
uint32 outBufLen = 0;
//Decode the minimum amount required to extract the NAL type.
if (sdp_decodebase64(paramSetStartBufVec[0],
MIN_ENCODED_BYTES,
decoded,
outBufLen,
sizeof(decoded)
))
{
uint8 nal_type = decoded[0] & AVC_NALTYPE_MASK;
switch (nal_type)
{
//Rather than modify every codec to accept SPS and PPS in any order,
//it is best to simply swap the order in the SDP parser so SPS
//always comes before PPS.
//If PPS comes before SPS, swap the order.
case AVC_NALTYPE_PPS:
{
uint8* tempBuf = paramSetStartBufVec.back();
paramSetStartBufVec.pop_back();
paramSetStartBufVec.push_front(tempBuf);
uint32 tempSize = paramSetBufSizeVec.back();
paramSetBufSizeVec.pop_back();
paramSetBufSizeVec.push_front(tempSize);
}
break;
case AVC_NALTYPE_SPS:
default:
break;
}
}
}
int configBufLength = aParamSetBufLen + numParamSets * (sizeof(uint16));
SDPAllocDestructDealloc<uint8> SDP_alloc;
uint8 *configPtr = SDP_alloc.allocate(configBufLength);
OsclRefCounterSA< SDPAllocDestructDealloc<uint8> > *refcnt =
new OsclRefCounterSA< SDPAllocDestructDealloc<uint8> >(configPtr);
OsclSharedPtr<uint8> parameterSetPtr(configPtr, refcnt);
uint32 offset = 0;
for (uint32 j = 0; j < paramSetStartBufVec.size(); j++)
{
uint8* inBuf = paramSetStartBufVec[j];
uint32 inBufLen = paramSetBufSizeVec[j];
uint32 outBufLen = 0;
uint8* paramSetSizePtr = configPtr + offset;
offset += 2; // for param set size
uint8* outPtr = configPtr + offset;
uint32 maxOutBufLen = configBufLength - offset;
if (!sdp_decodebase64(inBuf,
inBufLen,
outPtr,
outBufLen,
maxOutBufLen))
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseParameterSets : unable to set decodebase"));
return SDP_BAD_MEDIA_FMTP;
}
uint16 len = (uint16)outBufLen;
oscl_memcpy(paramSetSizePtr, &len, sizeof(uint16));
offset += outBufLen;
}
H264PayloadSpecificInfoType* payloadPtr =
(H264PayloadSpecificInfoType*)aH264MediaInfo->getPayloadSpecificInfoTypePtr(aPayLoadNumber);
if (payloadPtr == NULL)
{
PVMF_SDP_PARSER_LOGERROR((0, "SDPH264MediaInfoParser::parseParameterSets - Unable to find payload ptr for payload"));
return SDP_PAYLOAD_MISMATCH;
}
PVMF_SDP_PARSER_LOGINFO((0, "SDPH264MediaInfoParser::parseParameterSets - processing payload number : %d", aPayLoadNumber));
payloadPtr->setDecoderSpecificInfo(parameterSetPtr);
payloadPtr->setDecoderSpecificInfoSize(offset);
//if sample rate is zero override with defaults
Oscl_Vector<PayloadSpecificInfoTypeBase*, SDPParserAlloc> payloadSpecificInfoVector =
aH264MediaInfo->getPayloadSpecificInfoVector();
for (int ii = 0; ii < (int)payloadSpecificInfoVector.size();ii++)
{
if (payloadSpecificInfoVector[ii]->getSampleRate() == 0)
{
payloadSpecificInfoVector[ii]->sampleRate =
PVMF_SDP_DEFAULT_H264_SAMPLE_RATE;
}
}
return SDP_SUCCESS;
}