/* ------------------------------------------------------------------ * Copyright (C) 1998-2009 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 "sdp_parser.h" #include "sdp_mediaparser_registry.h" #include "oscl_string_utils.h" #include "oscl_string_containers.h" #include "oscl_str_ptr_len.h" #include "base_media_info_parser.h" #include "aac_media_info_parser.h" #include "amr_media_info_parser.h" #include "h263_media_info_parser.h" #include "m4v_media_info_parser.h" #include "still_image_media_info_parser.h" #include "pcma_media_info_parser.h" #include "pcmu_media_info_parser.h" #include "oscl_vector.h" #include "oscl_dll.h" OSCL_DLL_ENTRY_POINT_DEFAULT() struct mime_payload_pair { OsclMemoryFragment mime; Oscl_Vector<int, SDPParserAlloc> payload_no; }; OSCL_EXPORT_REF SDP_Parser::SDP_Parser(SDPMediaParserRegistry*& regTable, bool sipSdp): iLogger(NULL), _pSDPMediaParserRegistry(regTable), mediaArrayIndex(0), applicationFlag(false), isSipSdp(sipSdp) { iLogger = PVLogger::GetLoggerObject("SDP_Parser"); } OSCL_EXPORT_REF SDP_Parser::~SDP_Parser() { } bool SDP_Parser::parse_rtpmap(const char *start, const char *end, int& rtp_payload, OsclMemoryFragment& encoding_name) { const int len_of_rtpmap = 9; // grab the endpoints const char *sptr = start + len_of_rtpmap; const char *eptr; // skip to the first whitespace character eptr = skip_to_whitespace(sptr, end); if (eptr < sptr) { return false; } uint32 rtpPayload; if (PV_atoi(sptr, 'd', (eptr - sptr), rtpPayload) == false) { return false; } rtp_payload = (int)rtpPayload; // now get the encoding name sptr = skip_whitespace(eptr, end); if (sptr >= end) { return false; } // now skip to end of the encoding name for (eptr = sptr; eptr < end && (*eptr != ' ' && *eptr != '\t' && *eptr != '/'); ++eptr); if (eptr >= end) { return false; } encoding_name.ptr = (void *) sptr; encoding_name.len = eptr - sptr; return true; } int SDP_Parser::validate_media_line(const char *start, const char *end, Oscl_Vector<int, SDPParserAlloc>& payload_type, uint32& portNumber) { int len; const char *sptr, *eptr; sptr = start + 2; // start after the "m=" // skip to end of media type eptr = skip_to_whitespace(sptr, end); if (eptr >= end) { return 0; } len = eptr - sptr; // make sure type is supported if (!oscl_CIstrncmp(sptr, "audio", len) || !oscl_CIstrncmp(sptr, "video", len) || !oscl_CIstrncmp(sptr, "application", len)) { // the type is supported // make sure there is only one payload type in the format list // skip to start of port number sptr = skip_whitespace(eptr, end); if (sptr >= end) { return 0; } // skip to end of port number eptr = skip_to_whitespace(sptr, end); if (eptr <= sptr) { return 0; } const char *tmp_end_ptr = sptr; const char SDP_FWD_SLASH[] = "/"; OSCL_HeapString<SDPParserAlloc> restOfLine(tmp_end_ptr, eptr - tmp_end_ptr); const char *slash = oscl_strstr(restOfLine.get_cstr(), SDP_FWD_SLASH); if (slash == NULL) { // Get the port number if (PV_atoi(sptr, 'd', (eptr - sptr), portNumber) == false) { return 0; } } else { // Get the port number if (PV_atoi(restOfLine.get_cstr(), 'd', (slash - restOfLine.get_cstr()), portNumber) == false) { return 0; } } // skip to start of transport sptr = skip_whitespace(eptr, end); if (sptr >= end) { return 0; } // skip to end of transport eptr = skip_to_whitespace(sptr, end); if (eptr <= sptr) { return 0; } // skip to start of format list sptr = skip_whitespace(eptr, end); if (sptr >= end) { return 0; } // skip to end of first payload arg eptr = skip_to_whitespace(sptr, end); if (eptr <= sptr) { return 0; } // record the payload type for non-application m= lines if (oscl_strncmp(start + 2, "application", len)) { uint32 payloadType; while (sptr < end) { if (PV_atoi(sptr, 'd', (eptr - sptr), payloadType) == false) { return 0; } payload_type.push_back(payloadType); sptr = skip_to_whitespace(sptr, end); sptr = skip_whitespace_and_line_term(eptr, end); eptr = skip_whitespace_and_line_term(eptr, end); eptr = skip_to_whitespace(eptr, end); } } else { uint32 len = OSCL_MIN((uint32)(eptr - start), oscl_strlen("IMAGE")); if (!oscl_strncmp(start, "IMAGE", len)) { applicationFlag = true; } else //don't support this media. so skip the section return 0; } if (sptr < end) { return 0; } return 1; } return 0; } OSCL_EXPORT_REF SDP_ERROR_CODE SDP_Parser::parseSDP(const char *sdpText, int text_length, SDPInfo *sdp) { int index = 0, sdpIndex = 0; const char *end_ptr = sdpText + text_length ; // Point just beyond the end const char *section_start_ptr; const char *section_end_ptr; const char *line_start_ptr, *line_end_ptr; bool session_info_parsed = false; /**************************************************************************/ // The purpose of this outer loop is to partition the SDP into different // sections to be passed off to session-level parsers or media-level parsers. // We just need to find the boundaries and pass the appropriate sections // of code to the subparsers. sdpIndex = 0; index = 0; // these track whether media and session-level sections have already // been found int media_sections_found = 0; int session_section_found = 0; // skip any leading whitespace including line terminators section_start_ptr = skip_whitespace_and_line_term(sdpText, end_ptr); while ((section_start_ptr - sdpText) < text_length) { if (!get_next_line(section_start_ptr, end_ptr, line_start_ptr, line_end_ptr)) { break; } // figure out the type of section if (!oscl_strncmp(line_start_ptr, "v=", 2)) { // this is the session-level if (media_sections_found || session_section_found) { // there were already media sections or already a session-level section // so a session-level section at this point is not allowed. PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Duplicate Session Sections")); return SDP_BAD_FORMAT; } section_end_ptr = line_end_ptr; // record that the session-level section has been found session_section_found = 1; while (get_next_line(section_end_ptr, end_ptr, line_start_ptr, line_end_ptr)) { // check if this is the start of another section if (!oscl_strncmp(line_start_ptr, "v=", 2) || !oscl_strncmp(line_start_ptr, "m=", 2)) { break; } section_end_ptr = line_end_ptr; } OsclMemoryFragment session_frag; session_frag.ptr = (void *)section_start_ptr; session_frag.len = section_end_ptr - section_start_ptr; SDP_ERROR_CODE retval = parseSDPSessionInfo(section_start_ptr, section_end_ptr - section_start_ptr, sdp); if (retval != SDP_SUCCESS) { PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - parseSDPSessionInfo Failed=%d", retval)); return retval; } else { session_info_parsed = true; } } else if (!oscl_strncmp(line_start_ptr, "m=", 2)) { // now look for the end of the section section_end_ptr = line_end_ptr; bool supported_media = true; ++media_sections_found; sdp->setSegmentCount(media_sections_found); /* SUPPORTING MULTIPLE PAYLOAD TYPE PER MEDIA NOW */ // check to see how many payload types are present Oscl_Vector<int, SDPParserAlloc> payload_type; Oscl_Vector<int, SDPParserAlloc> rtpmap_pt; Oscl_Vector<OsclMemoryFragment, SDPParserAlloc> encoding_name_vector; uint32 portNumber = 0; if (!validate_media_line(line_start_ptr, line_end_ptr, payload_type, portNumber)) { // skip this section supported_media = false; } else { PVMF_SDP_PARSER_LOGINFO((0, "SDP_Parser::parseSDP - Validated MediaSection")); } int rtpmap_lines = 0; // get the next line OsclMemoryFragment encoding_name; encoding_name.ptr = NULL; encoding_name.len = 0; Oscl_Vector<int, SDPParserAlloc> AltId; while (get_next_line(section_end_ptr, end_ptr, line_start_ptr, line_end_ptr)) { // check if this is the start of another section if (!oscl_strncmp(line_start_ptr, "v=", 2) || !oscl_strncmp(line_start_ptr, "m=", 2)) { break; } if (supported_media && (applicationFlag == false)) { // check for lines which will give the media type // so the parser can be allocated. Simply look for // the "a=rtpmap" lines which contain the MIME type. StrPtrLen rtpmap_str("a=rtpmap:"); if (!oscl_strncmp(line_start_ptr, rtpmap_str.c_str(), rtpmap_str.length())) { ++rtpmap_lines; int rtpmap_cu; // get encoding name if (!parse_rtpmap(line_start_ptr, line_end_ptr, rtpmap_cu, encoding_name)) { // invalid format PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - parse_rtpmap Failed")); return SDP_BAD_MEDIA_FORMAT; } rtpmap_pt.push_back(rtpmap_cu); encoding_name_vector.push_back(encoding_name); OSCL_StackString<15> mime((const char*)(encoding_name.ptr), encoding_name.len); PVMF_SDP_PARSER_LOGINFO((0, "SDP_Parser::parseSDP - a=rtpmap mime=%s", mime.get_cstr())); } StrPtrLen alt_def("a=alt-default-id:"); if (!oscl_strncmp(line_start_ptr, alt_def.c_str(), alt_def.length())) { uint32 id; const char *sptr = line_start_ptr + alt_def.length(); sptr = skip_whitespace(sptr, line_end_ptr); if (!PV_atoi(sptr, 'd', line_end_ptr - sptr, id)) { PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Parsing a=alt-default-id: Failed")); return SDP_BAD_MEDIA_ALT_ID; } AltId.push_back(id); } StrPtrLen alt_id("a=alt:"); if (!oscl_strncmp(line_start_ptr, alt_id.c_str(), alt_id.length())) { uint32 id; const char *sptr = line_start_ptr + alt_id.length(); sptr = skip_whitespace(sptr, line_end_ptr); const char *eptr = sptr; for (; *eptr != ':'; eptr++); if (!PV_atoi(sptr, 'd', eptr - sptr, id)) { PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Parsing a=alt: Failed")); return SDP_BAD_MEDIA_ALT_ID; } if (AltId.back() != (int)id) AltId.push_back(id); } // fmtp and framesize payload check is provided below // this is done to make sure that the payload coming in these fields // is one of the payloads in the m= segment StrPtrLen fmtp("a=fmtp:"); if (!oscl_strncmp(line_start_ptr, fmtp.c_str(), fmtp.length())) { uint32 payload; const char *sptr = line_start_ptr + fmtp.length(); sptr = skip_whitespace(sptr, line_end_ptr); const char* eptr = skip_to_whitespace(sptr, line_end_ptr); if (!PV_atoi(sptr, 'd', eptr - sptr, payload)) { PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Parsing a=fmtp: Failed")); return SDP_BAD_MEDIA_FORMAT; } // The format is proper match the payload with payloads in m= segment bool matched = false; for (uint32 ii = 0; ii < payload_type.size(); ii++) { if (payload == (uint32)payload_type[ii]) { matched = true; break; } } if (!matched) { PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Payload Mismatch in fmtp line")); return SDP_PAYLOAD_MISMATCH; } } StrPtrLen framesize("a=framesize:"); if (!oscl_strncmp(line_start_ptr, framesize.c_str(), framesize.length())) { uint32 payload; const char *sptr = line_start_ptr + framesize.length(); sptr = skip_whitespace(sptr, line_end_ptr); const char* eptr = skip_to_whitespace(sptr, line_end_ptr); if (!PV_atoi(sptr, 'd', eptr - sptr, payload)) { PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Parsing a=framesize: Failed")); return SDP_BAD_MEDIA_FORMAT; } // The format is proper match the payload with payloads in m= segment bool matched = false; for (uint32 ii = 0; ii < payload_type.size(); ii++) { if (payload == (uint32)payload_type[ii]) { matched = true; break; } } if (!matched) { PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Payload Mismatch in a=framesize: line")); return SDP_PAYLOAD_MISMATCH; } } } // end if media is supported else { PVMF_SDP_PARSER_LOGINFO((0, "SDP_Parser::parseSDP - Skipping over an entire m= section")); } section_end_ptr = line_end_ptr; } // end loop over the entire media section // The checking for rtpmap vs payloads is not required if the port number is 0 in case it is a sip sdp bool check_for_rtpmap = true; if (isSipSdp && portNumber == 0) { check_for_rtpmap = false; } // Checking for rtpmap with the payloads if (supported_media && check_for_rtpmap) { // Validate the payload type and rtpmap if required int static_payload_count = 0; int ii = 0; for (; ii < (int)payload_type.size(); ii++) { // If any payload_type is in static range we do not care // for the rtpmap field. We will process for this static payload type sdp // the dynamic payload type if any will be ignored if it's rtpmap // is missing if ((payload_type[ii] >= FIRST_STATIC_PAYLOAD) && (payload_type[ii] <= LAST_STATIC_PAYLOAD)) { PVMF_SDP_PARSER_LOGINFO((0, "SDP_Parser::parseSDP - Static Payload =%d", payload_type[ii])); static_payload_count++; } } if (static_payload_count == 0) { // The payload type present are all in the dynamic range if (rtpmap_pt.size() != payload_type.size()) { PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Mismatch between number of payloads and rtpmap lines")); return SDP_BAD_MEDIA_FORMAT; } for (int ii = 0; ii < (int)rtpmap_pt.size(); ii++) { if (rtpmap_pt[ii] != payload_type[ii]) { // this is an error PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Incorrect payload number in rtpmap line")); return SDP_PAYLOAD_MISMATCH; } } } else if (static_payload_count >= 1) { // All of the payloads can either be static or one of them // for every dynamic payload there should be matching rtpmap field if (rtpmap_pt.size() != (payload_type.size() - static_payload_count)) { PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Mismatch between number of payloads and rtpmap lines")); return SDP_BAD_MEDIA_FORMAT; } for (uint32 rtpmap_count = 0; rtpmap_count < rtpmap_pt.size(); rtpmap_count++) { bool match_found = false; for (int jj = 0; jj < (int)payload_type.size(); jj++) { if (rtpmap_pt[rtpmap_count] == payload_type[jj]) { match_found = true; break; } } if (match_found == false) { PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - No matching rtpmap line")); // this is an error return SDP_PAYLOAD_MISMATCH; } } } } if (session_info_parsed == false) { PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Missing Session Section")); return SDP_BAD_FORMAT; } if (supported_media) { StrPtrLen image("X-MP4V-IMAGE"); if (applicationFlag == true) { if (rtpmap_lines == 0) { rtpmap_lines++; encoding_name.ptr = (void *) image.c_str(); encoding_name.len = image.length(); applicationFlag = false; encoding_name_vector.push_back(encoding_name); } } // Compose all media encoding names and put them in one vector // this will carry all encoding names including static and dynamic PT StrPtrLen pcma(PVMF_MIME_PCMA); StrPtrLen pcmu(PVMF_MIME_PCMU); StrPtrLen amr("AMR"); Oscl_Vector<OsclMemoryFragment, SDPParserAlloc> all_media_encoding_names; uint32 ii = 0; uint32 jj = 0; for (; ii < payload_type.size(); ii++) { if (payload_type[ii] == PVMF_PCMU) { rtpmap_lines++; encoding_name.ptr = (void *) pcmu.c_str(); encoding_name.len = pcmu.length(); } else if (payload_type[ii] == PVMF_PCMA) { rtpmap_lines++; encoding_name.ptr = (void *) pcma.c_str(); encoding_name.len = pcma.length(); } else if (check_for_rtpmap == false) { // This means that the port is 0 and they payload is in dynamic range // rtpmap field is not present. // To map it internally let's put it under PCMU again rtpmap_lines++; encoding_name.ptr = (void *) pcmu.c_str(); encoding_name.len = pcmu.length(); } else { // All other payload encoding names are already present in the // encoding_name_vector. if (jj < encoding_name_vector.size()) { encoding_name = encoding_name_vector[jj]; jj++; } } all_media_encoding_names.push_back(encoding_name); } // compose all the above information in the following format // Any Mime type coming repeatedly with different payload numbers // Then club them together Oscl_Vector<mime_payload_pair, SDPParserAlloc> mime_payload_pair_vector; for (uint32 ll = 0; ll < payload_type.size(); ll++) { if (check_for_rtpmap == true) { bool matched = false; uint32 ii = 0; for (; ii < mime_payload_pair_vector.size(); ii++) { if (oscl_strncmp((char*)mime_payload_pair_vector[ii].mime.ptr, (char*)all_media_encoding_names[ll].ptr, all_media_encoding_names[ll].len) == 0) { matched = true; break; } } if (matched) { mime_payload_pair_vector[ii].payload_no.push_back(payload_type[ll]); } else { mime_payload_pair mpp; mpp.payload_no.push_back(payload_type[ll]); mpp.mime.len = all_media_encoding_names[ll].len; mpp.mime.ptr = all_media_encoding_names[ll].ptr; mime_payload_pair_vector.push_back(mpp); } } else { // It is a sip sdp with port = 0. Hence do not make specific checks mime_payload_pair mpp; mpp.payload_no.push_back(payload_type[ll]); mpp.mime.len = all_media_encoding_names[ll].len; mpp.mime.ptr = all_media_encoding_names[ll].ptr; mime_payload_pair_vector.push_back(mpp); } } if (rtpmap_lines >= 1) { SDPMediaParserFactory *mediaParserFactory; SDPBaseMediaInfoParser *mediaParser; SDP_ERROR_CODE retval; for (uint32 kk = 0; kk < mime_payload_pair_vector.size(); kk++) { encoding_name = mime_payload_pair_vector[kk].mime; if ((mediaParserFactory = _pSDPMediaParserRegistry->lookupSDPMediaParserFactory(encoding_name)) != NULL) { mediaParser = mediaParserFactory->createSDPMediaParserInstance(); if (mediaParser == NULL) { PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Unable to create media parser")); return SDP_FAILURE; } if (AltId.size() > 0) { int alt_id; bool alt_def_id = false; for (int ss = 0; ss < (int)AltId.size(); ss++) { if (ss == 0) alt_def_id = true; else alt_def_id = false; alt_id = AltId[ss]; if ((retval = mediaParser->parseMediaInfo(section_start_ptr, section_end_ptr - section_start_ptr, sdp, mime_payload_pair_vector[kk].payload_no, isSipSdp, alt_id, alt_def_id)) != SDP_SUCCESS) { OSCL_StackString<8> mime((const char*)(encoding_name.ptr), encoding_name.len); PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Parsing m= section failed, mime=%s", mime.get_cstr())); OSCL_DELETE((mediaParser)); sdp->freeLastMediaInfoObject(); return retval; } } } else { if ((retval = mediaParser->parseMediaInfo(section_start_ptr, section_end_ptr - section_start_ptr, sdp, mime_payload_pair_vector[kk].payload_no, isSipSdp)) != SDP_SUCCESS) { OSCL_StackString<8> mime((const char*)(encoding_name.ptr), encoding_name.len); PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Parsing m= section failed, mime=%s", mime.get_cstr())); OSCL_DELETE((mediaParser)); sdp->freeLastMediaInfoObject(); return retval; } } sdp->IncrementAlternateMediaInfoVectorIndex(); OSCL_DELETE((mediaParser)); } mediaParser = NULL; } // End of for } if (rtpmap_lines == 0) // no rtpmap found in media { PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Parsing m= section failed, No rtpmap line")); return SDP_BAD_MEDIA_MISSING_RTPMAP; } } } // end this is a media section else { PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Unrecognized Syntax")); // unknown section type -- this is an error return SDP_FAILURE; } section_start_ptr = skip_whitespace_and_line_term(section_end_ptr, end_ptr); } { //for SDP which doesn't have session level range, set the session level range //to be the MAX of media ranges. if (NULL == sdp->getSessionInfo()) { PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Missing Session Info")); return SDP_BAD_FORMAT; } RtspRangeType *mySdpRange = ((RtspRangeType *)sdp->getSessionInfo()->getRange()); if (NULL == mySdpRange) { PVMF_SDP_PARSER_LOGERROR((0, "SDP_Parser::parseSDP - Unable to retrieve session range")); return SDP_BAD_FORMAT; } if (mySdpRange->format == RtspRangeType::INVALID_RANGE) { PVMF_SDP_PARSER_LOGINFO((0, "SDP_Parser::parseSDP - No Valid Session Range - Setting it to Max of all media ranges")); for (int32 i = sdp->getNumMediaObjects() - 1; i >= 0; i--) { Oscl_Vector<mediaInfo*, SDPParserAlloc> mediaInfoVec = sdp->getMediaInfo(i); for (uint32 j = 0; j < mediaInfoVec.size(); j++) { mediaInfo* mInfo = mediaInfoVec[j]; if (mInfo == NULL) { continue; } const RtspRangeType *mInfoSdpRange = mInfo->getRtspRange(); if (NULL == mInfoSdpRange) { continue; } if (mInfoSdpRange->format != RtspRangeType::NPT_RANGE) { continue; } if (mySdpRange->format == RtspRangeType::INVALID_RANGE) { *mySdpRange = *mInfoSdpRange; } if (!mInfoSdpRange->end_is_set) {//live streaming *mySdpRange = *mInfoSdpRange; mySdpRange->start_is_set = true;//just to make sure mySdpRange->npt_start.npt_format = NptTimeFormat::NOW; return SDP_SUCCESS; } if (mInfoSdpRange->npt_start.npt_format == NptTimeFormat::NPT_SEC) { if (mInfoSdpRange->npt_start.npt_sec.sec < mySdpRange->npt_start.npt_sec.sec) { mySdpRange->npt_start.npt_sec = mInfoSdpRange->npt_start.npt_sec; } else if ((mInfoSdpRange->npt_start.npt_sec.sec == mySdpRange->npt_start.npt_sec.sec) && ((mInfoSdpRange->npt_start.npt_sec.milli_sec < mySdpRange->npt_start.npt_sec.milli_sec))) { mySdpRange->npt_start.npt_sec = mInfoSdpRange->npt_start.npt_sec; } } if (mInfoSdpRange->npt_end.npt_format == NptTimeFormat::NPT_SEC) { if (mInfoSdpRange->npt_end.npt_sec.sec > mySdpRange->npt_end.npt_sec.sec) { mySdpRange->npt_end.npt_sec = mInfoSdpRange->npt_end.npt_sec; } else if ((mInfoSdpRange->npt_end.npt_sec.sec == mySdpRange->npt_end.npt_sec.sec) && ((mInfoSdpRange->npt_end.npt_sec.milli_sec > mySdpRange->npt_end.npt_sec.milli_sec))) { mySdpRange->npt_end.npt_sec = mInfoSdpRange->npt_end.npt_sec; } } } } } } return SDP_SUCCESS; } OSCL_EXPORT_REF SDP_ERROR_CODE SDP_Parser::parseSDPDownload(const char *sdpText, int length, SDPInfo *sdp, movieInfo *mv) { SDP_ERROR_CODE retval = parseSDP(sdpText, length, sdp); if (retval != SDP_SUCCESS) { mv->trackCount = 0; mv->movieName[0] = '\0'; mv->creationDate[0] = '\0'; return retval; } else { /*Get Movie name*/ int len = oscl_strlen(sdp->getSessionInfo()->getSessionName()); if (len >= MAX_STRING_LEN) { oscl_strncpy(mv->movieName, sdp->getSessionInfo()->getSessionName(), (MAX_STRING_LEN - 1)); mv->movieName[MAX_STRING_LEN-1] = '\0'; } else { oscl_strncpy(mv->movieName, sdp->getSessionInfo()->getSessionName(), len); mv->movieName[len] = '\0'; } /*Get creation date*/ len = oscl_strlen(sdp->getSessionInfo()->getCreationDate()); if (len >= MAX_STRING_LEN) { oscl_strncpy(mv->creationDate, sdp->getSessionInfo()->getCreationDate(), (MAX_STRING_LEN - 1)); mv->creationDate[MAX_STRING_LEN-1] = '\0'; } else { oscl_strncpy(mv->creationDate, sdp->getSessionInfo()->getCreationDate(), len); mv->creationDate[len] = '\0'; } /*Get number of tracks*/ mv->trackCount = sdp->getNumMediaObjects(); /*Get start stop times*/ convertToMilliSec(*sdp->getSessionInfo()->getRange(), mv->duration.startTime, mv->duration.stopTime); /*Get MIMEType and other track info*/ for (int ii = 0; ii < mv->trackCount; ii++) { Oscl_Vector<mediaInfo*, SDPParserAlloc> mediaInfoVec = sdp->getMediaInfo(ii); /* * There would only be one element in this vector * for fast track download content. */ mediaInfo* minfo = mediaInfoVec[0]; mv->TrackArray[ii].bitrate = minfo->getBitrate(); len = oscl_strlen(minfo->getMIMEType()); if (len >= MAX_STRING_LEN) { oscl_strncpy(mv->TrackArray[ii].codec_type, minfo->getMIMEType(), (MAX_STRING_LEN - 1)); mv->TrackArray[ii].codec_type[MAX_STRING_LEN-1] = '\0'; } else { oscl_strncpy(mv->TrackArray[ii].codec_type, minfo->getMIMEType(), len); mv->TrackArray[ii].codec_type[len] = '\0'; } const char *trackID = minfo->getControlURL(); int track = 0; if (trackID != NULL) { const char *locateID = oscl_strstr(trackID, "="); if (locateID != NULL) { locateID += 1; uint32 atoi_tmp; PV_atoi(locateID, 'd', atoi_tmp); track = atoi_tmp; } } mv->TrackArray[ii].trackID = track; } } return SDP_SUCCESS; } int SDP_Parser::convertToMilliSec(RtspRangeType range , int &startTime, int &stopTime) { switch (range.format) { case RtspRangeType::NPT_RANGE: { if (range.start_is_set) { switch (range.npt_start.npt_format) { case NptTimeFormat::NOW: { startTime = 0; } break; case NptTimeFormat::NPT_SEC: { startTime = (int)(1000 * ((float)range.npt_start.npt_sec.sec + range.npt_start.npt_sec.milli_sec)); } break; case NptTimeFormat::NPT_HHMMSS: { startTime = 3600000 * range.npt_start.npt_hhmmss.hours + 60000 * range.npt_start.npt_hhmmss.min + 1000 * range.npt_start.npt_hhmmss.sec + (int)(10 * range.npt_start.npt_hhmmss.frac_sec); } break; } } else { startTime = 0; } if (range.end_is_set) { switch (range.npt_end.npt_format) { case NptTimeFormat::NOW: { stopTime = 0; } break; case NptTimeFormat::NPT_SEC: { stopTime = (int)(1000 * ((float)range.npt_end.npt_sec.sec + range.npt_end.npt_sec.milli_sec)); } break; case NptTimeFormat::NPT_HHMMSS: { stopTime = 3600000 * range.npt_end.npt_hhmmss.hours + 60000 * range.npt_end.npt_hhmmss.min + 1000 * range.npt_end.npt_hhmmss.sec + (int)(100 * range.npt_end.npt_hhmmss.frac_sec); } break; } } else { stopTime = false; } } break; case RtspRangeType::SMPTE_RANGE: case RtspRangeType::SMPTE_25_RANGE: case RtspRangeType::SMPTE_30_RANGE: { if (range.start_is_set) { startTime = 3600000 * range.smpte_start.hours + 60000 * range.smpte_start.minutes + 1000 * range.smpte_start.seconds; } else { startTime = 0; } if (range.end_is_set) { stopTime = 3600000 * range.smpte_end.hours + 60000 * range.smpte_end.minutes + 1000 * range.smpte_end.seconds; } else { stopTime = 0; } } break; case RtspRangeType::ABS_RANGE: { startTime = 0; stopTime = 0; } break; case RtspRangeType::UNKNOWN_RANGE: case RtspRangeType::INVALID_RANGE: { startTime = 0; stopTime = 0; return -1; } // break; This statement was removed to avoid compiler warning for Unreachable Code default: { startTime = 0; stopTime = 0; return -1; } // break; This statement was removed to avoid compiler warning for Unreachable Code } return 0; } OSCL_EXPORT_REF int SDP_Parser::getNumberOfTracks() { return mediaArrayIndex; } OSCL_EXPORT_REF int SDP_Parser::setNumberOfTracks(int tracks) { return (mediaArrayIndex = tracks); }