static int sendPktUdpRtp(STREAM_XMIT_NODE_T *pStream, unsigned int idxDest, const unsigned char *pData, unsigned int len) { int rc = 0; if(!pStream->pXmitAction->do_output_rtphdr) { if(len >= RTP_HEADER_LEN) { pData += RTP_HEADER_LEN; len -= RTP_HEADER_LEN; } else { len = 0; } } if(len == 0) { LOG(X_WARNING("sendto called with 0 length")); return 0; } pStream->pRtpMulti->pdests[idxDest].rtcp.sr.rtpts = pStream->pRtpMulti->pRtp->timeStamp; pStream->pRtpMulti->pdests[idxDest].rtcp.pktcnt++; pStream->pRtpMulti->pdests[idxDest].rtcp.octetcnt += len ; //fprintf(stderr, "sendto [dest:%d] %s:%d %d bytes seq:%u ts:%u ssrc:0x%x\n", idxDest, inet_ntoa(pStream->saDsts[idxDest].sin_addr), ntohs(pStream->saDsts[idxDest].sin_port), len, htons(pStream->pRtpMulti[idxDest].m_pRtp->sequence_num), htonl(pStream->pRtpMulti[idxDest].m_pRtp->timeStamp), pStream->pRtpMulti[idxDest].m_pRtp->ssrc); if((rc = sendto(STREAM_RTP_FD(pStream->pRtpMulti->pdests[idxDest]), (void *) pData, len, 0, (struct sockaddr *) &pStream->pRtpMulti->pdests[idxDest].saDsts, sizeof(pStream->pRtpMulti->pdests[idxDest].saDsts))) != len) { LOG(X_ERROR("sendto %s:%d for %d bytes failed with "ERRNO_FMT_STR), inet_ntoa(pStream->pRtpMulti->pdests[idxDest].saDsts.sin_addr), ntohs(pStream->pRtpMulti->pdests[idxDest].saDsts.sin_port), len, ERRNO_FMT_ARGS); return -1; } return rc; }
int mediadb_parseprefixes(const char *prefixstr, const char *prefixOutArr[], unsigned int maxPrefix) { unsigned int prefixIdx = 0; const char *p, *p2; int inside = 0; //TODO: this should use strutil_parse_csv if(!prefixstr) { return prefixIdx; } p = p2 = prefixstr; while(*p2 != '\0') { if(!inside && *p2 == '"') { p = ++p2; inside = 1; } if(inside && *p2 == '"') { if(prefixIdx >= maxPrefix) { LOG(X_WARNING("Max number %d of prefixes reached"), maxPrefix); break; } prefixOutArr[prefixIdx++] = (char *) p; *((char *) p2) = '\0'; inside = 0; } p2++; } return prefixIdx; }
SESSION_DESCR_T *session_add(SESSION_CACHE_T *pCache, struct sockaddr *pSockAddr) { SESSION_DESCR_T *pSession = NULL; SESSION_DESCR_T *pSessionPrev = NULL; unsigned int numSessions = 0; char tmp[128]; //TODO: need a hash while(pSession) { if(pSession->cookie[0] == '\0') { break; } numSessions++; pSessionPrev = pSession; pSession = pSession->pnext; } if(!pSession) { if(numSessions < SESSION_CACHE_MAX) { pSession = avc_calloc(1, sizeof(SESSION_DESCR_T)); if(pSessionPrev) { pSessionPrev->pnext = pSession; } else { pCache->plist = pSession; } } else { LOG(X_WARNING("No available session for %s:%d"), FORMAT_NETADDR(*pSockAddr, tmp, sizeof(tmp)), ntohs(PINET_PORT(pSockAddr))); return NULL; } } pSession->lastTm = time(NULL); return pSession; }
int xcode_frame_vid(IXCODE_VIDEO_CTXT_T *pIn, unsigned char *bufIn, unsigned int lenIn, IXCODE_OUTBUF_T *pout) { XCODE_IPC_MEM_T *pMem; IXCODE_VIDEO_CTXT_T *pXcodeV; unsigned int lenCopied; int rc; unsigned char *bufOut = pout->buf; unsigned int lenOut = pout->lenbuf; if(!pIn->common.pIpc) { return -1; } if(bufIn && bufIn != bufOut) { LOG(X_ERROR("buffer output does not match input")); return -1; } pMem = ((XCODE_IPC_DESCR_T *) pIn->common.pIpc)->pmem; pXcodeV = &pMem->hdr.ctxt.vid; pXcodeV->common.inpts90Khz = pIn->common.inpts90Khz; pXcodeV->common.outpts90Khz = pIn->common.outpts90Khz; pXcodeV->common.decodeInIdx = pIn->common.decodeInIdx; pXcodeV->common.decodeOutIdx = pIn->common.decodeOutIdx; pXcodeV->common.encodeInIdx = pIn->common.encodeInIdx; pXcodeV->common.encodeOutIdx = pIn->common.encodeOutIdx; pXcodeV->out[0].cfgForceIDR = pIn->out[0].cfgForceIDR; pXcodeV->out[0].qTot = pIn->out[0].qTot; pXcodeV->out[0].qSamples = pIn->out[0].qSamples; pXcodeV->out[0].pts = pIn->out[0].pts; pXcodeV->out[0].dts = pIn->out[0].dts; if((lenCopied = lenIn) > XCODE_IPC_MEM_DATASZ(pMem)) { lenCopied = XCODE_IPC_MEM_DATASZ(pMem); LOG(X_WARNING("Video frame length to be decoded truncated from %d to %d"), lenIn, lenCopied); } if(bufIn) { memcpy(XCODE_IPC_MEM_DATA(pMem), bufIn, lenCopied); } pMem->hdr.cmdrc = (enum IXCODE_RC) lenCopied; pMem->hdr.offsetOut = 0; if((rc = xcode_call_ipc(pIn->common.pIpc, XCODE_IPC_CMD_ENCODE_VID)) != -1) { pIn->common.inpts90Khz = pXcodeV->common.inpts90Khz; pIn->common.outpts90Khz = pXcodeV->common.outpts90Khz; pIn->common.decodeInIdx = pXcodeV->common.decodeInIdx; pIn->common.decodeOutIdx = pXcodeV->common.decodeOutIdx; pIn->common.encodeInIdx = pXcodeV->common.encodeInIdx; pIn->common.encodeOutIdx = pXcodeV->common.encodeOutIdx; pIn->out[0].qTot = pXcodeV->out[0].qTot; pIn->out[0].qSamples = pXcodeV->out[0].qSamples; pIn->out[0].frameType = pXcodeV->out[0].frameType; pIn->out[0].pts = pXcodeV->out[0].pts; pIn->out[0].dts = pXcodeV->out[0].dts; if((lenCopied = rc) > XCODE_IPC_MEM_DATASZ(pMem)) { lenCopied = XCODE_IPC_MEM_DATASZ(pMem); LOG(X_WARNING("Encoded video frame length truncated from %d to %d"), rc, lenCopied); rc = lenCopied; } else if(lenCopied > lenOut) { lenCopied = lenOut; LOG(X_WARNING("Encoded video frame length truncated from %d to %d"), rc, lenCopied); rc = lenCopied; } memcpy(bufOut, XCODE_IPC_MEM_DATA(pMem), lenCopied); } return rc; }
static int update_fileentry(FILE_LIST_T *pFileList, const char *mediaName, const char *mediaPath, FILE_LIST_ENTRY_T **ppEntry) { MEDIA_DESCRIPTION_T mediaDescr; FILE_STREAM_T fileStream; FILE_LIST_ENTRY_T *pFileEntryCur = NULL; FILE_LIST_ENTRY_T *pFileEntryNew = NULL; FILE_LIST_ENTRY_T fileEntry; char buf[VSX_MAX_PATH_LEN]; struct stat st; struct stat st2; int havestat = 0; size_t szt; int fileChanged = 1; memset(&fileEntry, 0, sizeof(fileEntry)); //fprintf(stdout, "looking for '%s'\n", mediaName); if((pFileEntryCur = file_list_find(pFileList, mediaName))) { //fprintf(stdout, "found entry '%s' numTn:%d\n", pFileEntryCur->name, pFileEntryCur->numTn); // mark the entry as current to avoid deletion pFileEntryCur->flag = 1; if((havestat = !fileops_stat(mediaPath, &st)) == 0) { LOG(X_ERROR("Unable to stat '%s'"), mediaPath); } else if(st.st_size == pFileEntryCur->size && st.st_ctime == pFileEntryCur->tm) { fileChanged = 0; // // Special case to probe for recent creation .seek file // since the last time this entry was written // if(pFileEntryCur->duration == 0) { buf[sizeof(buf) - 1] = '\0'; strncpy(buf, mediaPath, sizeof(buf) - 1); if((szt = strlen(buf))) { strncpy(&buf[szt], MP2TS_FILE_SEEKIDX_NAME, sizeof(buf) - szt - 1); } if(fileops_stat(buf, &st2) == 0) { fileChanged = 1; } } } pFileEntryNew = pFileEntryCur; } else { // Check for invalid chars - ',' is used as a delimeter in .avcfidx if(strchr(mediaName, ',') || mediaName[0] == '#' || mediaName[0] == ' ' || mediaName[0] == '=') { LOG(X_WARNING("Illegal filename '%s' not added to database"), mediaPath); pFileEntryNew = NULL; fileChanged = 0; } else { LOG(X_DEBUG("Could not find database entry '%s'"), mediaPath); pFileEntryNew = &fileEntry; } } if(fileChanged) { if(OpenMediaReadOnly(&fileStream, mediaPath) == 0) { memset(&mediaDescr, 0, sizeof(mediaDescr)); if(filetype_getdescr(&fileStream, &mediaDescr, 1) == 0) { pFileEntryNew->flag = 1; pFileEntryNew->duration = mediaDescr.durationSec; pFileEntryNew->size = mediaDescr.totSz; if(!havestat && (havestat = !fileops_stat(mediaPath, &st)) == 0) { LOG(X_ERROR("Unable to stat new entry '%s'"), mediaPath); } if(havestat) { pFileEntryNew->tm = st.st_ctime; //fprintf(stderr, "-----------set tm:%ld %s\n", pFileEntryNew->tm, mediaPath); } if(mediaDescr.haveVid) { create_viddescrstr(&mediaDescr, pFileEntryNew->vstr, sizeof(pFileEntryNew->vstr)); } if(mediaDescr.haveAud) { create_auddescrstr(&mediaDescr, pFileEntryNew->astr, sizeof(pFileEntryNew->astr)); //fprintf(stdout, "aud descr:'%s'\n", pFileEntryNew->astr); } } else { pFileEntryNew->flag = 1; pFileEntryNew->size = fileStream.size; } CloseMediaFile(&fileStream); } // end of OpenMediaReadOnly if(pFileEntryCur == NULL) { pFileEntryNew->name[sizeof(pFileEntryNew->name) - 1] = '\0'; strncpy(pFileEntryNew->name, mediaName, sizeof(pFileEntryNew->name) - 1); pFileEntryCur = file_list_addcopy(pFileList, pFileEntryNew); } } // end of if(fileChanged) if(ppEntry) { *ppEntry = pFileEntryCur; } return fileChanged; }
static int h264_decodeSlice(H264_DECODER_CTXT_T *pCtxt, BIT_STREAM_T *pStream, uint8_t nalType) { int first_mb_in_slice = 0; unsigned int slice_type = 0; unsigned int slice_type0 = 0; unsigned int pps_id = 0; unsigned int frame_idx = 0; int idr_pic_id = 0; int picture_structure = 0; int tmp; H264_NAL_SPS_T *pSps; H264_NAL_PPS_T *pPps; if(pStream->sz - pStream->byteIdx < 4) { LOG(X_WARNING("Slice given length to decode below minimum: %d"), pStream->sz - pStream->byteIdx); // Fail gracefully to accomodate empty NALs return H264_RC_OK; } pCtxt->sliceIdx++; first_mb_in_slice = bits_readExpGolomb(pStream); if((slice_type0 = slice_type = (unsigned int) bits_readExpGolomb(pStream)) > H264_SLICE_TYPE_SI) { slice_type -= (H264_SLICE_TYPE_SI + 1); } if(slice_type > H264_SLICE_TYPE_SI) { LOG(X_WARNING("Invalid slice type: %d"), slice_type); return H264_RC_ERR; } pCtxt->lastSliceType = slice_type; if((pps_id = bits_readExpGolomb(pStream)) >= H264_MAX_PPS_CNT || !pCtxt->pps[pps_id].is_ok) { LOG(X_WARNING("Slice references unknown pps:%d"), pps_id); return -1; } pPps = &pCtxt->pps[pps_id]; pSps = &pCtxt->sps[ pPps->seq_param_set_id ]; if((frame_idx = bits_read(pStream, pSps->log2_max_frame_num)) != pCtxt->frameIdx) { pCtxt->frameIdx = frame_idx; pCtxt->sliceIdx = 0; } if(!pSps->frame_mbs_only_flag && bits_read(pStream, 1)) { // field pic flag picture_structure = 1 + bits_read(pStream, 1); // pic_order_cnt_lsb } else { picture_structure = 3; } if(nalType == NAL_TYPE_IDR) { idr_pic_id = bits_readExpGolomb(pStream); } if(pSps->pic_order_cnt_type == 0) { pCtxt->picOrderCnt = bits_read(pStream, pSps->log2_max_pic_order_cnt_lsb); if(pPps->pic_order_present_flag == 1 && picture_structure == 3) { bits_readExpGolomb(pStream); // delta_poc_bottom } } else if(pSps->pic_order_cnt_type == 1 && !pSps->pic_delta_pic_order) { bits_readExpGolomb(pStream); // delta_poc[0] if(pPps->pic_order_present_flag == 1 && picture_structure == 3) { bits_readExpGolomb(pStream); // delta_poc[1] } } if(pPps->redundant_pic_cnt_present_flag) { bits_readExpGolomb(pStream); // bits_readExpGolomb(pStream); } //fprintf(stderr, "slice: first_mb_in_slice:%d, slice_type:%d,%d, sps:%d, pps:%d, fridx:%d, picstruct:%d, idr_pic_id:%d, sps poctype:%d, poc:%d, pps_pic_order_present:%d, pps_redundant_pic_cnt_present:%d\n", first_mb_in_slice, slice_type, slice_type0, pPps->seq_param_set_id, pps_id, frame_idx, picture_structure, idr_pic_id, pSps->pic_order_cnt_type, pCtxt->picOrderCnt, pPps->pic_order_present_flag, pPps->redundant_pic_cnt_present_flag); if(slice_type != H264_SLICE_TYPE_I) { if(slice_type != H264_SLICE_TYPE_B) { bits_read(pStream, 1); // direct_spatial_mv_pred } if((bits_read(pStream, 1))) { // num_ref_idx_active_override_flag bits_readExpGolomb(pStream); // ref_count[0] + 1 if(slice_type != H264_SLICE_TYPE_B) { bits_readExpGolomb(pStream); // ref_count[1] + 1 } } if(pPps->entropy_coding_mode_flag) { bits_readExpGolomb(pStream); } } if(slice_type == H264_SLICE_TYPE_SP) { bits_read(pStream, 1); // sp_for_switch_flag } if(slice_type == H264_SLICE_TYPE_SP || slice_type == H264_SLICE_TYPE_SI) { bits_readExpGolomb(pStream); // slice_qs_delta } if(pPps->deblocking_filter_control_present_flag) { tmp = bits_readExpGolomb(pStream); if(tmp < 2) { tmp^=1; } if(tmp) { bits_readExpGolomb(pStream); // slice_alpha_c0_offset bits_readExpGolomb(pStream); // slice_beta_offset } } return H264_RC_OK; }
void streamxmit_async_proc(void *pArg) { STREAMXMIT_WRAP_T wrap; int rc; STREAM_RTP_MULTI_T *pRtp = NULL; STREAM_RTP_DEST_T *pDest; unsigned int idx; struct timespec ts; struct timeval tv; int haveRequestedRetransmission = 0; memcpy(&wrap, (STREAMXMIT_WRAP_T *) pArg, sizeof(wrap)); logutil_tid_add(pthread_self(), wrap.tid_tag); pRtp = wrap.pRtp; pRtp->asyncRtpRunning = 1; LOG(X_DEBUG("RTP output thread started")); while(pRtp->asyncRtpRunning == 1 && !g_proc_exit) { gettimeofday(&tv, NULL); if(haveRequestedRetransmission) { ts.tv_sec = tv.tv_sec; ts.tv_nsec = tv.tv_usec + 20000; //LOG(X_DEBUG("RTP output thread going to wait for 20ms..")); } else { ts.tv_sec = tv.tv_sec + 2; ts.tv_nsec = tv.tv_usec; //LOG(X_DEBUG("RTP output thread going to wait for 2 sec..")); } if((rc = pthread_cond_timedwait(&pRtp->asyncRtpCond.cond, &pRtp->asyncRtpCond.mtx, &ts)) < 0) { LOG(X_WARNING("RTP output thread pthread_cond_timedwait failed")); usleep(20000); } if(pRtp->asyncRtpRunning != 1 || g_proc_exit) { break; } haveRequestedRetransmission = 0; pthread_mutex_lock(&pRtp->mtx); //LOG(X_DEBUG("RTP output thread doing its thing..")); if(pRtp->numDests > 0) { for(idx = 0; idx < pRtp->maxDests; idx++) { pDest = &pRtp->pdests[idx]; //if(pDest->isactive) { LOG(X_DEBUG("ACTIVE[%d]"), idx); stream_abr_notifyitrate(pDest->pstreamStats, &pDest->streamStatsMtx, STREAM_ABR_UPDATE_REASON_NACK_REQ, .5f); } if(pDest->isactive && pDest->asyncQ.haveRequestedRetransmission) { if((rc = streamxmit_retransmitRtp(pDest)) > 0) { haveRequestedRetransmission = 1; } } } } pthread_mutex_unlock(&pRtp->mtx); } LOG(X_DEBUG("RTP output thread ending")); pRtp->asyncRtpRunning = -1; logutil_tid_remove(pthread_self()); }
int http_gethttplive(CAP_ASYNC_DESCR_T *pCfg, CAPTURE_CBDATA_T *pStreamsOut, CAPTURE_STREAM_T *pStream, const char *puri, HTTP_RESP_T *pHttpResp, HTTP_PARSE_CTXT_T *pHdrCtxt) { int rc = 0; char *path; const char *pbuf = pHdrCtxt->pbuf; unsigned int szbuf = pHdrCtxt->szbuf; const char *pm3ubuf; pthread_t ptd; pthread_attr_t attr; HTTPLIVE_CLIENT_T client; int highestIdx, lowestIdx, firstIdx; memset(&client, 0, sizeof(client)); client.pCfg = pCfg; NETIOSOCK_FD(client.netsock) = INVALID_SOCKET; client.netsock.flags = pCfg->pSockList->netsockets[0].flags; client.pStreamsOut = pStreamsOut; client.pStream = pStream; client.running = -1; pthread_mutex_init(&client.mtx, NULL); do { //fprintf(stderr, "HTTP_GETHTTPLIVE sock: %d\n", NETIOSOCK_FD(pCfg->pSockList->netsockets[0])); if(NETIOSOCK_FD(pCfg->pSockList->netsockets[0]) == INVALID_SOCKET) { //fprintf(stderr, "GOING TO CONNECT FOR M3U...\n"); if((rc = httpcli_connect(&pCfg->pSockList->netsockets[0], &pCfg->pSockList->salist[0], "HTTPLive playlist thread")) < 0) { break; } memset(pHdrCtxt, 0, sizeof(HTTP_PARSE_CTXT_T)); memset(pHttpResp, 0, sizeof(HTTP_RESP_T)); pHdrCtxt->pnetsock = &pCfg->pSockList->netsockets[0]; pHdrCtxt->pbuf = pbuf; pHdrCtxt->szbuf = szbuf; pHdrCtxt->tmtms = 0; } if((rc = mediadb_getdirlen(puri)) > 0) { if(rc >= sizeof(client.uriprefix)) { rc = sizeof(client.uriprefix) - 1; } memcpy(client.uriprefix, puri, rc); } if((pm3ubuf = get_m3u8(pCfg, puri, pHttpResp, pHdrCtxt, (unsigned char *) pbuf, szbuf))) { m3u_free(&client.pl, 0); memset(&client.pl, 0, sizeof(client.pl)); /* pm3ubuf="#EXTM3U\r\n" "#EXT-X-VERSION:3\r\n" "#EXT-X-ALLOW-CACHE:NO\r\n" "#EXT-X-TARGETDURATION:14\r\n" "#EXT-X-MEDIA-SEQUENCE:2699\r\n" "#EXTINF:10,\r\n" "media_2699.ts?wowzasessionid=1144297750\r\n" "#EXTINF:10,\r\n" "media_2700.ts?wowzasessionid=1144297750\r\n" "#EXTINF:7,\r\n" "media_2701.ts?wowzasessionid=1144297750\r\n"; */ VSX_DEBUGLOG("Got m3u contents '%s'", pm3ubuf); //fprintf(stderr, "Got m3u contents '%s'\n", pm3ubuf); pthread_mutex_lock(&client.mtx); rc = m3u_create_buf(&client.pl, pm3ubuf); pthread_mutex_unlock(&client.mtx); if(rc > 0) { path = NULL; lowestIdx = find_lowest_idx(&client.pl, NULL); highestIdx = find_highest_idx(&client.pl, NULL); if(!client.insession) { if(highestIdx < 0) { LOG(X_WARNING("Unable to find httplive starting index from %s"), puri); } else { // // Some segmentors may write the last chunk to the playlist file even though // the chunk is still being appended on disk // //if(highestIdx > lowestIdx) { // firstIdx = highestIdx - 1; //} else { firstIdx = highestIdx; //} client.curidx = firstIdx; client.nextidx = client.curidx; client.insession = 1; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); client.running = 1; if(pthread_create(&ptd, &attr, (void *) httplive_mediaproc, &client) != 0) { LOG(X_ERROR("Unable to create httplive media download thread")); rc = -1; client.running = -1; } } } else { // client.insession //fprintf(stderr, "check playlist falling behind curidx:%d nextidx:%d, lowestIdx:%d, highestIdx:%d\n", client.curidx, client.nextidx, lowestIdx, highestIdx); // // Prevent falling behind playlist, a warning will be printed in the ts get thread // pthread_mutex_lock(&client.mtx); if(client.nextidx < lowestIdx) { client.nextidx = lowestIdx; } pthread_mutex_unlock(&client.mtx); } //if(rc >= 0) { // m3u_dump(&client.pl); //} } } else { // get_m3u8 rc = -1; } netio_closesocket(&pCfg->pSockList->netsockets[0]); if(rc >= 0) { //fprintf(stderr, "M3U SLEEPING FOR %d\n", client.pl.targetDuration); if(client.pl.targetDuration > 0) { sleep(client.pl.targetDuration); } else { sleep(9); } } } while(rc >= 0); client.insession = 0; while(client.running != -1) { usleep(20000); } m3u_free(&client.pl, 0); pthread_mutex_destroy(&client.mtx); return rc; }
int cbparse_entry_metafile(void *pArg, const char *p) { int rc = 0; PARSE_ENTRY_DATA_T *pEntryData = (PARSE_ENTRY_DATA_T *) pArg; if(!strncasecmp(p, METAFILE_KEY_FILE, strlen(METAFILE_KEY_FILE))) { store_parse_entry(p, METAFILE_KEY_FILE, &pEntryData->flags, PARSE_FLAG_HAVE_FILENAME, pEntryData->filename, sizeof(pEntryData->filename)); } else if(!strncasecmp(p, METAFILE_KEY_RTMP_PROXY, strlen(METAFILE_KEY_RTMP_PROXY))) { store_parse_entry(p, METAFILE_KEY_RTMP_PROXY, &pEntryData->flags, PARSE_FLAG_HAVE_RTMP_PROXY, pEntryData->rtmpproxy, sizeof(pEntryData->rtmpproxy)); } else if(!strncasecmp(p, METAFILE_KEY_RTSP_PROXY, strlen(METAFILE_KEY_RTSP_PROXY))) { store_parse_entry(p, METAFILE_KEY_RTSP_PROXY, &pEntryData->flags, PARSE_FLAG_HAVE_RTSP_PROXY, pEntryData->rtspproxy, sizeof(pEntryData->rtspproxy)); } else if(!strncasecmp(p, METAFILE_KEY_HTTP_PROXY, strlen(METAFILE_KEY_HTTP_PROXY))) { store_parse_entry(p, METAFILE_KEY_HTTP_PROXY, &pEntryData->flags, PARSE_FLAG_HAVE_HTTP_PROXY, pEntryData->httpproxy, sizeof(pEntryData->httpproxy)); } else if(!strncasecmp(p, METAFILE_KEY_HTTPLINK, strlen(METAFILE_KEY_HTTPLINK))) { store_parse_entry(p, METAFILE_KEY_HTTPLINK, &pEntryData->flags, PARSE_FLAG_HAVE_HTTPLINK, pEntryData->link, sizeof(pEntryData->link)); } else if(!strncasecmp(p, METAFILE_KEY_INPUT, strlen(METAFILE_KEY_INPUT))) { store_parse_entry(p, METAFILE_KEY_INPUT, &pEntryData->flags, PARSE_FLAG_HAVE_INPUT, pEntryData->input, sizeof(pEntryData->input)); } else if(!strncasecmp(p, METAFILE_KEY_DEVICE, strlen(METAFILE_KEY_DEVICE))) { store_parse_entry(p, METAFILE_KEY_DEVICE, &pEntryData->flags, PARSE_FLAG_HAVE_DEVNAME, pEntryData->devname, sizeof(pEntryData->devname)); } else if(!strncasecmp(p, METAFILE_KEY_PROFILE, strlen(METAFILE_KEY_PROFILE))) { store_parse_entry(p, METAFILE_KEY_PROFILE, &pEntryData->flags, PARSE_FLAG_HAVE_PROFILE, pEntryData->profile, sizeof(pEntryData->profile)); } else if(!strncasecmp(p, METAFILE_KEY_SHARED, strlen(METAFILE_KEY_SHARED))) { if((p = store_parse_entry(p, METAFILE_KEY_SHARED, &pEntryData->flags, PARSE_FLAG_HAVE_SHARED, NULL, 0))) { if(IS_CONF_VAL_TRUE(p)) { pEntryData->shared = BOOL_ENABLED_OVERRIDE; } else if(IS_CONF_VAL_FALSE(p)) { pEntryData->shared = BOOL_DISABLED_OVERRIDE; } } } else if(!strncasecmp(p, METAFILE_KEY_SECURE, strlen(METAFILE_KEY_SECURE))) { if((p = store_parse_entry(p, METAFILE_KEY_SECURE, &pEntryData->flags, PARSE_FLAG_HAVE_SECURE, NULL, 0))) { if(IS_CONF_VAL_TRUE(p)) { pEntryData->secure = BOOL_ENABLED_OVERRIDE; } else if(IS_CONF_VAL_FALSE(p)) { pEntryData->secure = BOOL_DISABLED_OVERRIDE; } } } else if(!strncasecmp(p, METAFILE_KEY_XCODEARGS, strlen(METAFILE_KEY_XCODEARGS))) { store_parse_entry(p, METAFILE_KEY_XCODEARGS, &pEntryData->flags, PARSE_FLAG_HAVE_XCODEARGS, pEntryData->xcodestr, sizeof(pEntryData->xcodestr)); } else if(!strncasecmp(p, METAFILE_KEY_DIGESTAUTH, strlen(METAFILE_KEY_DIGESTAUTH))) { store_parse_entry(p, METAFILE_KEY_DIGESTAUTH, &pEntryData->flags, PARSE_FLAG_HAVE_DIGESTAUTH, pEntryData->userpass, sizeof(pEntryData->userpass)); //TODO: verify ascii chars delimited by colon... } else if(!strncasecmp(p, METAFILE_KEY_TOKEN, strlen(METAFILE_KEY_TOKEN))) { store_parse_entry(p, METAFILE_KEY_TOKEN, &pEntryData->flags, PARSE_FLAG_HAVE_TOKEN, pEntryData->tokenId, sizeof(pEntryData->tokenId)); } else if(!strncasecmp(p, METAFILE_KEY_METHODS, strlen(METAFILE_KEY_METHODS))) { p = store_parse_entry(p, METAFILE_KEY_METHODS, &pEntryData->flags, PARSE_FLAG_HAVE_METHODS, NULL, 0); strutil_parse_csv(cbparse_methods_csv, pEntryData, p); } else if(!strncasecmp(p, METAFILE_KEY_ID, strlen(METAFILE_KEY_ID))) { store_parse_entry(p, METAFILE_KEY_ID, &pEntryData->flags, PARSE_FLAG_HAVE_ID, pEntryData->id, sizeof(pEntryData->id)); } else if(!strncasecmp(p, METAFILE_KEY_IGNORE, strlen(METAFILE_KEY_IGNORE))) { store_parse_entry(p, METAFILE_KEY_IGNORE, &pEntryData->flags, PARSE_FLAG_HAVE_IGNORE, pEntryData->ignore, sizeof(pEntryData->ignore)); } else if(!strncasecmp(p, METAFILE_KEY_TITLE, strlen(METAFILE_KEY_TITLE))) { store_parse_entry(p, METAFILE_KEY_TITLE, &pEntryData->flags, PARSE_FLAG_HAVE_TITLE, pEntryData->title, sizeof(pEntryData->title)); // // 'description' has been deprecated in favor of 'title' // } else if(!strncasecmp(p, METAFILE_KEY_DESCRIPTION, strlen(METAFILE_KEY_DESCRIPTION))) { store_parse_entry(p, METAFILE_KEY_DESCRIPTION, &pEntryData->flags, PARSE_FLAG_HAVE_TITLE, pEntryData->title, sizeof(pEntryData->title)); } else { LOG(X_WARNING("Unhandled metafile parameter '%s' in %s line:%d"), p, pEntryData->path, pEntryData->linenum); } return rc; }
int sdputil_createSdp(STREAMER_CFG_T *pCfg, SDP_DESCR_T *pSdp) { int rc = 0; unsigned char header[256]; unsigned int headerLen = sizeof(header); STREAM_XCODE_CTXT_T xcodeCtxt; unsigned int outidx = 0; SDP_CODEC_PARAM_T codecParam; union { SPSPPS_RAW_T spspps; } uvidhdr; if(!pCfg || !pSdp) { return -1; } memset(pSdp, 0, sizeof(SDP_DESCR_T)); memset(&uvidhdr, 0, sizeof(uvidhdr)); memset(&xcodeCtxt, 0, sizeof(xcodeCtxt)); xcodeCtxt.vidData.piXcode = &pCfg->xcode; if(!pCfg->novid && pCfg->xcode.vid.common.cfgDo_xcode) { codecParam.u.pktzMode = PKTZ_H264_MODE_NOTSET; if(sdputil_init(pSdp, codecType_getRtpPT(pCfg->xcode.vid.common.cfgFileTypeOut, pCfg->cfgrtp.payloadTypesMin1), 90000, // TODO: may vary pCfg->xcode.vid.common.cfgFileTypeOut, pCfg->pdestsCfg[0].dstHost, pCfg->pdestsCfg[0].ports[0], pCfg->pdestsCfg[0].portsRtcp[0], NULL, NULL, NULL, &codecParam, NULL, &pCfg->fbReq) < 0) { return -1; } if(!pCfg->xcode.vid.pUserData) { pCfg->xcode.vid.pUserData = &xcodeCtxt.vidUData; } rc = xcode_getvidheader(&xcodeCtxt.vidData, outidx, header, &headerLen); if(pCfg->xcode.vid.pUserData == &xcodeCtxt.vidUData) { pCfg->xcode.vid.pUserData = NULL; } if(rc == 0) { switch(pCfg->xcode.vid.common.cfgFileTypeOut) { case XC_CODEC_TYPE_H264: //TODO: this should be xcoder output index specific if((rc = xcode_h264_find_spspps(&xcodeCtxt.vidUData.out[outidx].uout.xh264, header, headerLen, &uvidhdr.spspps)) < 0) { return rc; } if(uvidhdr.spspps.sps_len <= 0) { LOG(X_WARNING("H.264 SPS not available")); return -1; } memcpy(pSdp->vid.u.h264.profile_level_id, &uvidhdr.spspps.sps[1], 3); memcpy(&pSdp->vid.u.h264.spspps, &uvidhdr.spspps, sizeof(SPSPPS_RAW_T)); break; default: break; } } } return rc; }
void mediadb_proc(void *parg) { MEDIADB_DESCR_T *pMediaDb = (MEDIADB_DESCR_T *) parg; struct stat st; char buf[VSX_MAX_PATH_LEN]; const char *avcthumb = NULL; size_t sz; int rc; unsigned int iterIdx = 0; if(!pMediaDb->mediaDir) { return; } memset(buf, 0, sizeof(buf)); sz = mediadb_getdirlen(pMediaDb->dbDir); strncpy(buf, pMediaDb->dbDir, sizeof(buf) - 1); buf[sz] = '\0'; LOG(X_DEBUG("Database dir: '%s'"), pMediaDb->dbDir); if(fileops_stat(buf, &st) != 0) { // // Create database root dir // LOG(X_DEBUG("Creating dir '%s'"), buf); if(fileops_MkDir(buf, S_IRWXU | S_IRWXG | S_IRWXO ) < 0) { LOG(X_CRITICAL("Unable to create database dir '%s'"), buf); return; } } if(fileops_stat(pMediaDb->dbDir, &st) != 0) { // // Create database subdir specific for mediaDir // LOG(X_DEBUG("Creating dir '%s'"), pMediaDb->dbDir); if(fileops_MkDir(pMediaDb->dbDir, S_IRWXU | S_IRWXG | S_IRWXO ) < 0) { LOG(X_CRITICAL("Unable to create database dir '%s'"), pMediaDb->dbDir); return; } } // // Check thumbnail creation script // if(pMediaDb->avcthumb) { if(fileops_stat(pMediaDb->avcthumb, &st) == 0) { // // Truncate the avcthumblog // if(pMediaDb->avcthumblog) { snprintf(buf, sizeof(buf), DATE_CMD" > %s", pMediaDb->avcthumblog); //LOG(X_DEBUG("Executing system command '%s'"), buf); rc = system(buf); if(fileops_stat(pMediaDb->avcthumblog, &st) != 0) { pMediaDb->avcthumblog = NULL; } } if(pMediaDb->lgTnWidth == 0 || pMediaDb->lgTnWidth == 0) { pMediaDb->lgTnWidth = LG_THUMB_WIDTH; pMediaDb->lgTnHeight = LG_THUMB_HEIGHT; } if(pMediaDb->smTnWidth == 0 || pMediaDb->smTnHeight == 0) { pMediaDb->smTnWidth = SM_THUMB_WIDTH; pMediaDb->smTnHeight = SM_THUMB_HEIGHT; } LOG(X_DEBUG("Using %s to generate thumbnails %dx%d %dx%d"), pMediaDb->avcthumb, pMediaDb->smTnWidth, pMediaDb->smTnHeight, pMediaDb->lgTnWidth, pMediaDb->lgTnHeight); } else { LOG(X_WARNING("Unable to find %s Video thumbnails disabled."), pMediaDb->avcthumb); pMediaDb->avcthumb = NULL; } } else { LOG(X_WARNING("Not using video thumbnails. Please set '%s=' in the configuration file."), SRV_CONF_KEY_AVCTHUMB); } // // Set avchumb to null and do not create thumbnails on first // iteration to allow all avcfidx files to be created quickly // avcthumb = pMediaDb->avcthumb; pMediaDb->avcthumb = NULL; if(g_proc_exit == 0) { iterate_subdirs(pMediaDb, pMediaDb->dbDir, pMediaDb->mediaDir, 1, 0); pMediaDb->avcthumb = avcthumb; usleep(2000000); } while(g_proc_exit == 0) { iterate_subdirs(pMediaDb, pMediaDb->dbDir, pMediaDb->mediaDir, 1, iterIdx++); usleep(10000000); } }
DIR_ENTRY_LIST_T *direntry_getentries(const MEDIADB_DESCR_T *pMediaDb, const char *dir, const char *fidxdir, const char *searchstr, int includedirs, unsigned int startidx, unsigned int max, enum DIR_SORT sort) { DIR *pdir; int rc = 0; struct dirent *direntry; char path[VSX_MAX_PATH_LEN]; struct stat st; int addEntry; FILE_LIST_T fileList; FILE_LIST_ENTRY_T *pFileEntry = NULL; META_FILE_T metaFile; DIR_ENTRY_LIST_T *pEntryList = NULL; DIR_ENTRY_T entry; DIR_ENTRY_T *pEntry; const char *pdispname; unsigned int idx = 0; unsigned int cnt = 0; unsigned int cntInDir = 0; COMPARE_DIR_ENTRY compareFunc = direntry_getcompfunc(sort); VSX_DEBUG_MGR( LOG(X_DEBUG("MGR - direntry_getentries dir: '%s', fidxdir: '%s', searchstr: '%s', " "includedirs: %d, startidx: %d, max: %d"), dir, fidxdir, searchstr, includedirs, startidx, max)); if(!(pdir = fileops_OpenDir(dir))) { return NULL; } memset(&fileList, 0, sizeof(fileList)); if(fidxdir) { file_list_read(fidxdir, &fileList); } // // Read the directory wide metafile to get a list of 'ignore' entries // memset(&metaFile, 0, sizeof(metaFile)); mediadb_prepend_dir(dir, METAFILE_DEFAULT, path, sizeof(path)); if(fileops_stat(path, &st) == 0) { metafile_open(path, &metaFile, 1, 1); } while((direntry = fileops_ReadDir(pdir))) { VSX_DEBUG_MGR( LOG(X_DEBUGV("MGR - direntry_getentries d_name: '%s', isdir: %d"), direntry->d_name, (direntry->d_type & DT_DIR)) ); if(is_entry_ignored(metaFile.pignoreList, direntry->d_name)) { continue; } if(!(pdispname = find_entry_description(metaFile.pDescriptionList, direntry->d_name))) { pdispname = direntry->d_name; } if(searchstr && !is_match_search(pdispname, NULL, searchstr)) { continue; } memset(&entry, 0, sizeof(entry)); strncpy(entry.d_name, direntry->d_name, sizeof(entry.d_name) - 1); if(pdispname != direntry->d_name) { strncpy(entry.displayname, pdispname, sizeof(entry.displayname) - 1); } entry.d_type = direntry->d_type; addEntry = 0; if(direntry->d_type & DT_DIR) { if(includedirs && mediadb_isvalidDirName(pMediaDb, direntry->d_name)) { addEntry = 1; } } else if(mediadb_isvalidFileName(pMediaDb, direntry->d_name, 1, 1)) { mediadb_prepend_dir(dir, direntry->d_name, path, sizeof(path)); if(fileops_stat(path, &st) == 0) { entry.size = st.st_size; //entry.tm = st.st_mtime; entry.tm = st.st_ctime; if(fidxdir && (pFileEntry = file_list_find(&fileList, direntry->d_name))) { entry.numTn = pFileEntry->numTn; entry.duration = pFileEntry->duration; } addEntry = 1; } } if(addEntry) { VSX_DEBUG_MGR( LOG(X_DEBUGV("MGR - direntry_getentries add d_name: '%s', isdir: %d"), direntry->d_name, (direntry->d_type & DT_DIR)) ); if(compareFunc || (idx >= startidx && (max == 0 || cnt < max))) { if(cnt >= DIR_ENTRY_LIST_BUFNUM) { LOG(X_WARNING("Not showing more than %d entries in %s"), cnt, dir); break; } else if(!(pEntry = direntry_addsorted(&pEntryList, &entry, compareFunc))) { LOG(X_ERROR("Failed to add directory entry '%s' to list"), direntry->d_name); rc = -1; break; } cnt++; } idx++; cntInDir++; } } // // Since when a sort is requested we have to sort every entry in the directory. Now we can move the head pointer // to the first desired entry // if(pEntryList && compareFunc && startidx > 0) { pEntry = pEntryList->pHead; for(idx = 0; idx < startidx; idx++) { pEntry = pEntry->pnext; cnt--; } pEntryList->pHead = pEntry; if(cnt > max) { cnt = max; } //fprintf(stderr, "moved phead to %s, cnt:%d, cntInDir:%d\n", pEntryList->pHead ? pEntryList->pHead->d_name : NULL, cnt, cntInDir); } fileops_CloseDir(pdir); if(fidxdir) { file_list_close(&fileList); } metafile_close(&metaFile); if(rc == 0 && !pEntryList) { // If the user requested an index out of bounds, return an empty list with // a valid cntTotalInDir pEntryList = (DIR_ENTRY_LIST_T *) avc_calloc(1, sizeof(DIR_ENTRY_LIST_T)); } if(pEntryList) { pEntryList->cntTotal = cnt; pEntryList->cntTotalInDir = cntInDir; } //if(pEntryList) fprintf(stderr, "DIR '%s' num:%d numAlloc:%d pnext:0x%x TOTAL:%d/%d\n", dir, pEntryList->num, pEntryList->numAlloc, pEntryList->pnext, pEntryList->cntTotal, pEntryList->cntTotalInDir); return pEntryList; }
static int readFramesFromDev(CAP_DEV_CFG_T *pCapDevCfg) { int rc = 0; TIME_VAL tv0, tv, tvSleep, tvStart; TIME_VAL tvElapsed = 0; TIME_VAL tvMark = 0; unsigned int frameIdx = 0; COLLECT_STREAM_PKTDATA_T pktdata; CAP_DEV_CTXT_T capDevCtxt; CAPTURE_FILTER_T *pFilter = &pCapDevCfg->pCapCfg->pcommon->filt.filters[pCapDevCfg->idxFilter]; //BMP_FILE_T bmp; //memset(&bmp, 0, sizeof(bmp)); memset(&capDevCtxt, 0, sizeof(capDevCtxt)); capDevCtxt.dev = pCapDevCfg->pCapCfg->pcommon->localAddrs[pCapDevCfg->idxFilter]; //capDevCtxt.dev = "./rawfmt.bmp"; if(readFramesSetup(pFilter, &capDevCtxt.frame) < 0) { return -1; } //TODO: seperate out and maintain better throttle mechanism tv0 = tvStart = timer_GetTime(); while(!g_proc_exit && pCapDevCfg->pCapCfg->running == 0) { // // Reading too slow, reset time reference // if(tvElapsed > tvMark + 300000) { LOG(X_WARNING("Resetting read time for %s after %llu ms > %llu ms (%.3ffps x %ufr)"), capDevCtxt.dev, tvElapsed / TIME_VAL_MS, (TIME_VAL)((1000.0f / pFilter->fps) * frameIdx), pFilter->fps, frameIdx); tv0 = timer_GetTime(); frameIdx = 0; } do { tv = timer_GetTime(); tvElapsed = (tv - tv0); tvMark = (frameIdx / pFilter->fps) * TIME_VAL_US; if(tvElapsed < tvMark) { if((tvSleep = (tvMark - tvElapsed)) < 1000) { tvSleep = 1; } //fprintf(stderr, "sleeping %lld ns\n", tvSleep); usleep(tvSleep); tv = timer_GetTime(); tvElapsed = (tv - tv0); } } while(tvElapsed < tvMark); //if(frameIdx > 50) usleep(50000); //fprintf(stderr, "reading frame %d supposed to be:%lld at:%lld\n", frameIdx, (TIME_VAL) (frameIdx*1000.0f/pFilter->fps),(timer_GetTime() - tv0)/ TIME_VAL_MS); if((rc = readFrameFromDev(&capDevCtxt)) < 0) { break; } /* if((rc = bmp_create(&bmp, pFilter->mediaType, pFilter->width, pFilter->height, capDevCtxt.frame.pData)) < 0) { LOG(X_ERROR("Failed to create bmp %d x %d for format %d"), pFilter->width, pFilter->height, pFilter->mediaType); break; } PKTLEN32(pktdata.payload) = BMP_GET_LEN(&bmp); pktdata.payload.pData = BMP_GET_DATA(&bmp); */ pktdata.tv.tv_sec = (tv - tvStart) / TIME_VAL_US; pktdata.tv.tv_usec = (tv - tvStart) % TIME_VAL_US; PKTLEN32(pktdata.payload) = capDevCtxt.frame.len; pktdata.payload.pData = capDevCtxt.frame.pData; //fprintf(stderr, "got %d bmp %d from %s %llu\n", capDevCtxt.frame.len, BMP_GET_LEN(&bmp), capDevCtxt.dev, tv - tvStart); pCapDevCfg->pStream->cbOnPkt(&pCapDevCfg->pStreamCbData->sp[0], (const COLLECT_STREAM_PKTDATA_T *) &pktdata); frameIdx++; } if(capDevCtxt.fd > 0) { close(capDevCtxt.fd); } free(capDevCtxt.frame.pBuf); //bmp_destroy(&bmp); return rc; }
int stream_net_h264_getSliceBytes(void *pArg, unsigned char *pBuf, unsigned int len) { STREAM_H264_T *pStreamH264 = (STREAM_H264_T *) pArg; uint32_t szSlice; unsigned int lenToRead = len; unsigned int bufIdx = 0; unsigned int fsIdxInSlice; if(pStreamH264->frame.pSlice == NULL) { return 0; } fsIdxInSlice = pStreamH264->frame.idxInSlice; szSlice = pStreamH264->frame.pSlice->content.sizeWr; if(pStreamH264->includeAnnexBHdr) { szSlice += 4; } if(len > szSlice - pStreamH264->frame.idxInSlice) { LOG(X_WARNING("Decreasing copy length from %u to (%u - %u)"), len, szSlice, pStreamH264->frame.idxInSlice); len = szSlice - pStreamH264->frame.idxInSlice; } if(pStreamH264->includeAnnexBHdr) { if(pStreamH264->frame.idxInSlice < 4) { while(pStreamH264->frame.idxInAnnexBHdr < 4 && bufIdx < len) { pBuf[bufIdx++] = (pStreamH264->frame.idxInAnnexBHdr == 3 ? 0x01 : 0x00); pStreamH264->frame.idxInAnnexBHdr++; lenToRead--; pStreamH264->frame.idxInSlice++; } } if(pStreamH264->frame.idxInSlice < 4) { return pStreamH264->frame.idxInSlice; } else { fsIdxInSlice = pStreamH264->frame.idxInSlice - 4; } } /* if(pStreamH264->frame.pSlice == &pStreamH264->audSlice) { if(fsIdxInSlice + lenToRead > 2) { LOG(X_ERROR("Attempt to read AUD[%d] + %d > AUD len: 2\n"), fsIdxInSlice, lenToRead, 2); return -1; } pBuf[bufIdx] = pStreamH264->audSlice.hdr; // // Mpeg4-Part10AVC Table 7-2 "Meaning of primary_pic_type" // 3 bits 0 - 7 ((7 << 5) = I,Si,P,SP,B possible slice types in primary coded picture) // pBuf[bufIdx + 1] = H264_AUD_I_SI_P_SP_B; } else */ if(pStreamH264->frame.pSlice == &pStreamH264->spsSlice) { if(fsIdxInSlice + lenToRead > pStreamH264->pH264->spspps.sps_len) { LOG(X_ERROR("Attempt to read sps[%d] + %d > sps len: %d\n"), fsIdxInSlice, lenToRead, pStreamH264->pH264->spspps.sps_len); return -1; } memcpy(&pBuf[bufIdx], &pStreamH264->pH264->spspps.sps[fsIdxInSlice], lenToRead); } else if(pStreamH264->frame.pSlice == &pStreamH264->ppsSlice) { if(fsIdxInSlice + lenToRead > pStreamH264->pH264->spspps.pps_len) { LOG(X_ERROR("Attempt to read pps[%d] + %d > pps len: %d\n"), fsIdxInSlice, lenToRead, pStreamH264->pH264->spspps.pps_len); return -1; } memcpy(&pBuf[bufIdx], &pStreamH264->pH264->spspps.pps[fsIdxInSlice], lenToRead); } else { if(SeekMediaFile(pStreamH264->frame.pFileStream, pStreamH264->frame.pSlice->content.fileOffset + fsIdxInSlice, SEEK_SET) != 0) { return -1; } if(ReadFileStream(pStreamH264->frame.pFileStream, &pBuf[bufIdx], lenToRead) < 0) { return -1; } } //if(pStreamH264->frame.idxInSlice <= 4) { //fprintf(stderr, "h264 frameId:%d\n", pStreamH264->frame.frameId); //if(pStreamH264->frame.pSlice->content.sizeWr > 65530) fprintf(stderr, "\n\n\n\nh264 size:%d\n", pStreamH264->frame.pSlice->content.sizeWr); //fprintf(stderr, "h264 size:%d\n", pStreamH264->frame.pSlice->content.sizeWr); //avc_dumpHex(stderr, pBuf, 16, 1); //} pStreamH264->frame.idxInSlice += lenToRead; return len; }
int flvsrv_create_vidframe_h264(BYTE_STREAM_T *bs, CODEC_VID_CTXT_T *pFlvV, const OUTFMT_FRAME_DATA_T *pFrame, uint32_t *pmspts, int32_t *pmsdts, int keyframe) { int rc = 0; unsigned int frameIdx = 0; uint32_t mspts = 0; int32_t msdts = 0; uint8_t frameType; unsigned char *pData = NULL; unsigned int lenData = 0; if(!pFlvV || !pFrame) { return -1; } if(!pmspts) { pmspts = &mspts; } if(!pmsdts) { pmsdts = &msdts; } // // Omit NAL AUD // if((rc = h264_check_NAL_AUD(OUTFMT_DATA(pFrame), OUTFMT_LEN(pFrame))) > 0) { frameIdx = rc; } if(OUTFMT_LEN(pFrame) < frameIdx) { LOG(X_ERROR("Invalid video frame length: %d"), OUTFMT_LEN(pFrame)); return -1; } if(OUTFMT_PTS(pFrame) > pFlvV->tmprev) { *pmspts = (uint32_t) (OUTFMT_PTS(pFrame) - pFlvV->tmprev) / 90.0f; } pFlvV->tmprev += (*pmspts * 90); //hdrsz = 8; // hdr size < 12 has relative timestamps //if(*pmspts > 50 || *pmspts <= 0) fprintf(stderr, "funky mspts:%d\n", *pmspts); //if(pFrame->pts > pFlvV->tm0) { // *pmspts = (pFrame->pts - pFlvV->tm0) / 90.0f; //} if(OUTFMT_DTS(pFrame) != 0) { *pmsdts = (int32_t) (OUTFMT_DTS(pFrame) / 90.0f); } //*pmsdts = *pmspts; //if(pFrame->pts > pRtmp->av.vid.tmprev) { // *pmsdts = (uint32_t) (pFrame->pts - pFlvV->tmprev) / 90.0f; //} //pFlvV->tmprev = pFrame->pts; //TODO: check if the dts is actually a +/- offset from the pts, or an absolute time //pFlvV->tmprev = pFrame->pts + pFrame->dts - pFlvV->tm0; //pFlvV->tmprev = pFrame->pts - pFlvV->tm0; //tm = (uint32_t) (pFlvV->tmprev / 90.0f); //tm = (pFlvV->tmprev / 90.0f) - *pmsdts; hdrsz = 8; // hdr size < 12 has relative timestamps if(OUTFMT_LEN(pFrame) - frameIdx > pFlvV->tmpFrame.sz) { LOG(X_ERROR("h264 frame size too large %d for buffer size %d"), OUTFMT_LEN(pFrame) - frameIdx, pFlvV->tmpFrame.sz); return -1; } memcpy(pFlvV->tmpFrame.buf, &OUTFMT_DATA(pFrame)[frameIdx], OUTFMT_LEN(pFrame) - frameIdx); pData = pFlvV->tmpFrame.buf; lenData = OUTFMT_LEN(pFrame) - frameIdx; //LOG(X_DEBUG("Before convert to h264b len:%d"), lenData); LOGHEX_DEBUG(pData, MIN(48, lenData)); // // Remove any SPS/PPS, which could potentially contain 3 byte start codes // pData = vidframe_h264_removenals(pData, &lenData); //LOG(X_DEBUG("lenData:%d, orig:%d"), lenData, OUTFMT_LEN(pFrame)); if(lenData <= 0) { LOG(X_WARNING("h264 flv/rtmp/mkv frame length is %d, original: %d"), lenData, OUTFMT_LEN(pFrame)); } h264_converth264bToAvc(pData, lenData); //LOG(X_DEBUG("After convert to h264b len:%d"), lenData); LOGHEX_DEBUG(pData, MIN(48, lenData)); /* //fprintf(stderr, "len:%d 0x%x 0x%x\n", lenData, pData[3], pData[4]); while(lenData > 4 && ((pData[4] & NAL_TYPE_MASK) == NAL_TYPE_SEQ_PARAM || (pData[4] & NAL_TYPE_MASK) == NAL_TYPE_PIC_PARAM)) { rc = (int) htonl( *(uint32_t *)pData ); pData += (4 + rc); lenData -= (4 + rc); //fprintf(stderr, "decrement vid size to:%d by 4 + %d\n", lenData, rc); } //fprintf(stderr, "now len:%d 0x%x 0x%x\n", lenData, pData[3], pData[4]); LOG(X_DEBUG("After convert to h264b and remove of seq params len:%d"), lenData); LOGHEX_DEBUG(pData, MIN(16, lenData)); */ //if((pData[4] & NAL_NRI_MASK) == NAL_NRI_HI) { // pData[4] &= ~NAL_NRI_MASK; // pData[4] |= NAL_NRI_MED; //} if(bs) { //bs.idx = hdrsz; //bs.buf = &pRtmp->out.buf[pRtmp->out.idx]; //bs.sz = pRtmp->out.sz - pRtmp->out.idx; frameType = keyframe ? FLV_VID_FRM_KEYFRAME : FLV_VID_FRM_INTERFRAME; bs->buf[bs->idx++] = (frameType << 4) | (FLV_VID_CODEC_AVC); bs->buf[bs->idx++] = FLV_VID_AVC_PKTTYPE_NALU; flv_write_int24(bs, *pmsdts); // composition time } pFlvV->tmpFrame.idx = (pData - pFlvV->tmpFrame.buf); return lenData; }
static const char *get_m3u8(CAP_ASYNC_DESCR_T *pCfg, const char *puri, HTTP_RESP_T *pHttpResp, HTTP_PARSE_CTXT_T *pHdrCtxt, unsigned char *pbuf, unsigned int szbuf) { int sz = 0; struct timeval tv0, tv1; unsigned int consumed = 0; unsigned int contentLen = 0; unsigned int tmtms = 0; unsigned char *pdata = NULL; gettimeofday(&tv0, NULL); //fprintf(stderr, "GET M3U... puri:'%s', hdrslen:%d\n", puri, pHdrCtxt->hdrslen); if(pHdrCtxt->hdrslen == 0) { if((httpcli_gethdrs(pHdrCtxt, pHttpResp, &pCfg->pSockList->salist[0], puri, http_getConnTypeStr(HTTP_CONN_TYPE_CLOSE), 0, 0, pCfg->pcommon->addrsExtHost[0], NULL)) < 0) { return NULL; } } if((pdata = http_get_contentlen_start(pHttpResp, pHdrCtxt, pbuf, szbuf, 1, &contentLen))) { consumed = pHdrCtxt->idxbuf - pHdrCtxt->hdrslen; } if(pdata && net_setsocknonblock(NETIOSOCK_FD(pCfg->pSockList->netsockets[0]), 1) < 0) { pdata = NULL; } //fprintf(stderr, "NET_RECV in m3u... %d < %d idxbuf:%d hdrslen:%d pdata:0x%x\n", consumed, contentLen, pHdrCtxt->idxbuf, pHdrCtxt->hdrslen, pdata); while(pdata && consumed < contentLen && !g_proc_exit) { if((sz = netio_recvnb(&pCfg->pSockList->netsockets[0], (unsigned char *) &pdata[consumed], contentLen - consumed, 500)) > 0) { consumed += sz; } //fprintf(stderr, "NET REceiving... %d < %d, %d tmtms:%d\n", consumed, contentLen, sz, tmtms); gettimeofday(&tv1, NULL); if(tmtms > 0 && consumed < contentLen && TIME_TV_DIFF_MS(tv1, tv0) > (unsigned int) tmtms) { LOG(X_WARNING("HTTP %s:%d%s timeout %d ms exceeded"), inet_ntoa(pCfg->pSockList->salist[0].sin_addr), ntohs(pCfg->pSockList->salist[0].sin_port), puri, tmtms); pdata = NULL; break; } } if(pdata && contentLen > 0 && consumed >= contentLen) { pdata[consumed] = '\0'; } else { pdata = NULL; } //fprintf(stderr, "GOT m3u...0x%x %d < %d\n", pdata, consumed, contentLen);avc_dumpHex(stderr, pdata, consumed, 1); return (const char *) pdata; }
static int createTn(const MEDIADB_DESCR_T *pMediaDb, FILE_LIST_ENTRY_T *pFileListEntry, const char *pathMedia, const char *pathTn, int numTn, int maxTnInIter) { int rc = 0; int highestTn = 0; struct stat st; unsigned int idx; char buf[VSX_MAX_PATH_LEN]; char cmd[VSX_MAX_PATH_LEN]; char cmdnice[64]; char tmpfile[VSX_MAX_PATH_LEN]; int numTnCreated = 0; char avcthumbsize[32]; unsigned int secOffset; const char *avcthumb = pMediaDb->avcthumb; const char *avcthumblog = pMediaDb->avcthumblog; const char *avcthumb_file = avcthumb; const char *avcthumblog2 = avcthumblog; #ifdef WIN32 char curdir[VSX_MAX_PATH_LEN]; char avcthumblogpath[VSX_MAX_PATH_LEN]; char avcthumblogpath2[VSX_MAX_PATH_LEN]; char avcthumblogprefix[64]; size_t sz; #endif // WIN32 #if defined(FFMPEG_THUMB_NEW) const char thumbsizedelim = ':'; #else // (FFMPEG_THUMB_NEW) const char thumbsizedelim = 'x'; #endif // (FFMPEG_THUMB_NEW) tmpfile[0] = '\0'; cmdnice[0] = '\0'; //fprintf(stdout, "createTn '%s' numTn:%d, maxTnIter:%d, TN_MAX:%d, g_proc_exit:%d (0x%x)\n", pathTn, numTn, maxTnInIter, SRVMEDIA_TN_MAX, g_proc_exit, &g_proc_exit); for(idx = 0; idx < SRVMEDIA_TN_MAX; idx++) { if(idx > 0) { secOffset = (idx - 1) * SRVMEDIA_TN_INTERVAL_SEC; } else { secOffset = 4; } if(pFileListEntry->duration == 0) { snprintf(tmpfile, sizeof(tmpfile), "%s%s"VSXTMP_EXT, pathTn, pMediaDb->tn_suffix); if(fileops_stat(tmpfile, &st) != 0) { if(numTn == 0 && idx == 0) { //fprintf(stdout, "creating tmp file '%s'\n", tmpfile); // // Create tmpfile which should only exist during // creation of thumbnails for source of unknown duration // Since the "tn=" parameter in the index file denotes // the current number of thumbnails available // if(fileops_touchfile(tmpfile) != 0) { LOG(X_ERROR("Failed to create %s"), tmpfile); } } else if((int) idx >= numTn) { //fprintf(stdout, "breaking out, have all the tns %d > %d\n", idx, numTn); break; } } else if(idx == SRVMEDIA_TN_MAX - 1) { //fprintf(stdout, "removing tmp file '%s'\n", tmpfile); fileops_DeleteFile(tmpfile); } } else if(pFileListEntry->duration > 0 && secOffset >= pFileListEntry->duration - 1) { break; } if(idx == 0) { snprintf(avcthumbsize, sizeof(avcthumbsize), "%d%c%d", pMediaDb->smTnWidth, thumbsizedelim, pMediaDb->smTnHeight); snprintf(buf, sizeof(buf), "%s%s.jpg", pathTn, pMediaDb->tn_suffix); } else { snprintf(avcthumbsize, sizeof(avcthumbsize), "%d%c%d", pMediaDb->lgTnWidth, thumbsizedelim, pMediaDb->lgTnHeight); snprintf(buf, sizeof(buf), "%s%s%u.jpg", pathTn, pMediaDb->tn_suffix, idx - 1); } if(fileops_stat(buf, &st) < 0 || st.st_size == 0) { #ifdef WIN32 // // cd into directory of avcthumb script // avcthumblogprefix[0] = '\0'; curdir[0] = '\0'; fileops_getcwd(curdir, sizeof(curdir)); if((sz = mediadb_getdirlen(avcthumb)) > 0) { if(sz >= sizeof(tmpfile)) { sz = sizeof(tmpfile) - 1; } tmpfile[sizeof(tmpfile) - 1] = '\0'; strncpy(tmpfile, avcthumb, sizeof(tmpfile)); if(sz > 0) { tmpfile[sz] = '\0'; fileops_setcwd(tmpfile); avcthumb_file = &avcthumb[sz]; snprintf(avcthumblogprefix, sizeof(avcthumblogprefix), "..\\"); } } if(avcthumblog) { snprintf(avcthumblogpath, sizeof(avcthumblogpath), "%s%s", avcthumblogprefix, avcthumblog); snprintf(avcthumblogpath2, sizeof(avcthumblogpath2), "%s%s.err", avcthumblogprefix, avcthumblog); avcthumblog2 = avcthumblogpath2; avcthumblog = avcthumblogpath; } #else snprintf(cmdnice, sizeof(cmdnice), "nice -n19 "); #endif // WIN32 #if defined(FFMPEG_THUMB_NEW) // //TODO: create multiple thumbs on one run... will speed thnigs up alot! // '-ss 4 -vf "thumbnail,scale=164:90,fps=1/4" -frames:v 8' // snprintf(cmd, sizeof(cmd), "%s%s -v warning -i \"%s\" -an -vf \"thumbnail,scale=%s\" " "-frames:v 1 -f image2 -vcodec mjpeg -ss %u \"%s\" %s%s %s%s", cmdnice, avcthumb_file, pathMedia, avcthumbsize, secOffset, buf, avcthumblog ? ">>" : "", avcthumblog ? avcthumblog : "", avcthumblog2 ? "2>>" : "", avcthumblog2 ? avcthumblog2 : ""); #else // (FFMPEG_THUMB_NEW) snprintf(cmd, sizeof(cmd), "%s%s -i \"%s\" -an -s \"%s\" " "-vframes 1 -f image2 -vcodec mjpeg -ss %u \"%s\" %s%s %s%s", cmdnice, avcthumb_file, pathMedia, avcthumbsize, secOffset, buf, avcthumblog ? ">>" : "", avcthumblog ? avcthumblog : "", avcthumblog2 ? "2>>" : "", avcthumblog2 ? avcthumblog2 : ""); #endif // (FFMPEG_THUMB_NEW) //if(numcmd++ > 2) { // return 0; //} LOG(X_DEBUG("Creating thumbnail '%s'"), cmd); //TODO: seems that we're not getting SIGINT in the parent here... need a way to spawn (async?) & wait/poll rc = system(cmd); #ifdef WIN32 if(curdir[0] != '\0') { fileops_setcwd(curdir); } #endif // WIN3 //LOG(X_DEBUG("DOING STAT ON '%s'"), buf); if(fileops_stat(buf, &st) == 0 && st.st_size > 0) { rc = 0; highestTn = idx + 1; if(maxTnInIter > 0 && ++numTnCreated >= maxTnInIter) { break; } } else { rc = -1; LOG(X_WARNING("Failed to generate thumbnail '%s'"), buf); if(pFileListEntry->duration == 0) { snprintf(tmpfile, sizeof(tmpfile), "%s%s"VSXTMP_EXT, pathTn, pMediaDb->tn_suffix); if(fileops_stat(tmpfile, &st) == 0) { fileops_DeleteFile(tmpfile); } } else { snprintf(tmpfile, sizeof(tmpfile), "%s%s."NOTN_EXT, pathTn, pMediaDb->tn_suffix); if(fileops_touchfile(tmpfile) != 0) { LOG(X_ERROR("Failed to create %s"), tmpfile); } } if(idx == 0) { return -1; } break; } } else { highestTn = idx + 1; } } return highestTn; }
static int onPkt_h264(CAPTURE_CBDATA_SP_T *pSp, CAPTURE_STORE_CBDATA_T *pCbData, const unsigned char *pData, const unsigned int len, int recurselevel) { int nalType; int rc = 0; uint16_t nalLen; unsigned int idx = 0; if(pData == NULL || len == 0) { // TODO: write empty NAL LOG(X_WARNING("RTP H.264 lost slice len:%d, %s, ssrc: 0x%x"), len, pSp->pStream->strSrcDst, pSp->pStream->hdr.key.ssrc); pSp->spFlags |= (CAPTURE_SP_FLAG_DAMAGEDFRAME | CAPTURE_SP_FLAG_PREVLOST); return 0; } else if(recurselevel > 10) { LOG(X_WARNING("RTP H.264 maximum STAP nesting level reached:%d"), recurselevel); return 0; } nalType = pData[idx] & NAL_TYPE_MASK; //fprintf(stderr, "nal: %d\n", nalType); avc_dumpHex(stderr, &pData[idx], 4, 0); switch(nalType) { case NAL_TYPE_IDR: case NAL_TYPE_SEI: // // Prepend any SPS/PPS (from SDP) if not present in this GOP // if(pSp->pStream->pFilter->u_seqhdrs.vid.u.h264.spspps.sps_len > 0 && !(pSp->spFlags & CAPTURE_SP_FLAG_H264_HAVESPS)) { if((rc = writeNALFromNet(pCbData, pSp->pStream->pFilter->u_seqhdrs.vid.u.h264.spspps.sps_buf, pSp->pStream->pFilter->u_seqhdrs.vid.u.h264.spspps.sps_len, 1)) < 0) { return rc; } pSp->spFlags |= CAPTURE_SP_FLAG_H264_HAVESPS; } if((pSp->spFlags & CAPTURE_SP_FLAG_H264_HAVESPS) && pSp->pStream->pFilter->u_seqhdrs.vid.u.h264.spspps.pps_len > 0 && !(pSp->spFlags & CAPTURE_SP_FLAG_H264_HAVEPPS)) { if((rc = writeNALFromNet(pCbData, pSp->pStream->pFilter->u_seqhdrs.vid.u.h264.spspps.pps_buf, pSp->pStream->pFilter->u_seqhdrs.vid.u.h264.spspps.pps_len, 1)) < 0) { return rc; } pSp->spFlags |= CAPTURE_SP_FLAG_H264_HAVEPPS; } pSp->spFlags |= CAPTURE_SP_FLAG_KEYFRAME; if((rc = writeNALFromNet(pCbData, pData, len, 1)) < 0) { return rc; } break; case NAL_TYPE_NONSPECIFIC: case NAL_TYPE_SLICE: case NAL_TYPE_PARTITION_2: case NAL_TYPE_PARTITION_3: case NAL_TYPE_PARTITION_4: if((rc = writeNALFromNet(pCbData, pData, len, 1)) < 0) { return rc; } break; case NAL_TYPE_SEQ_PARAM: pSp->spFlags |= CAPTURE_SP_FLAG_H264_HAVESPS; if((rc = writeNALFromNet(pCbData, pData, len, 1)) < 0) { return rc; } break; case NAL_TYPE_PIC_PARAM: pSp->spFlags |= CAPTURE_SP_FLAG_H264_HAVEPPS; if((rc = writeNALFromNet(pCbData, pData, len, 1)) < 0) { return rc; } break; case NAL_TYPE_STAP_A: idx++; do { nalLen = htons(*((uint16_t *) &pData[idx])); idx += 2; if(nalLen + idx > len) { LOG(X_WARNING("RTP H.264 Invalid STAP-A NAL length: %d (%u/%d, "), nalLen, idx, len - idx); return -1; } else if(nalLen > 0) { if((rc = onPkt_h264(pSp, pCbData, &pData[idx], nalLen, recurselevel + 1)) < 0) { return rc; } } idx += nalLen; } while(idx + 3 < len); break; case NAL_TYPE_FRAG_A: // Frag Unit header 1 bit start, 1 bit end, 1 bit reserved=0, 5 bits nal type nalType = pData[++idx] & NAL_TYPE_MASK; if(nalType == NAL_TYPE_IDR) { pSp->spFlags |= CAPTURE_SP_FLAG_KEYFRAME; } if(pData[idx] & 0x80) { // start bit set pSp->spFlags |= CAPTURE_SP_FLAG_HAVEFRAGMENTSTART; //fprintf(stderr, "frag start nal: %d flag:%d\n", nalType, pSp->spFlags); avc_dumpHex(stderr, &pData[idx], 4, 0); ((unsigned char *)pData)[idx] &= 0x1f; ((unsigned char *)pData)[idx] |= (NAL_NRI_MASK & pData[idx - 1]); if((rc = writeNALFromNet(pCbData, &pData[idx], len - idx, 1)) < 0) { return rc; } } else { // // If the previous packet(s) were lost, and this is a fragment, // ensure it is not a fragment continuation // if(!(pSp->spFlags & CAPTURE_SP_FLAG_HAVEFRAGMENTSTART) || (pSp->spFlags & CAPTURE_SP_FLAG_PREVLOST)) { //fprintf(stderr, "setting H.264 DROPFRAME flag\n"); pSp->spFlags |= (CAPTURE_SP_FLAG_DAMAGEDFRAME | CAPTURE_SP_FLAG_DROPFRAME); } //fprintf(stderr, "frag cont nal: %d flag:%d\n", nalType, pSp->spFlags); avc_dumpHex(stderr, &pData[idx], 4, 0); idx++; if((rc = writeNALFromNet(pCbData, &pData[idx], len - idx, (pSp->spFlags & CAPTURE_SP_FLAG_HAVEFRAGMENTSTART) ? 0 : 1)) < 0) { return rc; } pSp->spFlags |= CAPTURE_SP_FLAG_HAVEFRAGMENTSTART; } break; default: LOG(X_WARNING("RTP H.264 Unsupported nal type: %d"), nalType); break; } return (int)(len - idx); }
static int iterate_subdirs(const MEDIADB_DESCR_T *pMediaDb, const char *pathDb, const char *pathMedia, int level, unsigned int iterIdx) { DIR *pdir; FILE_LIST_T fileList; FILE_LIST_ENTRY_T *pFileListEntry; struct dirent *direntry; char bufMedia[VSX_MAX_PATH_LEN]; char bufDb[VSX_MAX_PATH_LEN]; char tmpfile[VSX_MAX_PATH_LEN]; int rc = 0; int rcsub = 0; int fileListChanged = 0; int numTnCreated = 0; int highestTn; struct stat st; //fprintf(stdout, "%d dir:'%s' '%s'\n", level, pathDb, pathMedia); // // TODO: escape invalid chars in pathnames ' , & # $ ' // if(level >= SRVMEDIA_MAX_LEVELS) { LOG(X_WARNING("Path '%s' exceeded max number of subdirs under %s"), pathMedia, pMediaDb->mediaDir); return -1; } if(!(pdir = fileops_OpenDir(pathMedia))) { return -1; } memset(&fileList, 0, sizeof(fileList)); file_list_read(pathDb, &fileList); strncpy(fileList.pathmedia, pathMedia, sizeof(fileList.pathmedia) - 1); while((direntry = fileops_ReadDir(pdir)) && g_proc_exit == 0) { //fprintf(stdout, "- %d %s\n", level, direntry->d_name); if(direntry->d_type & DT_DIR) { if(mediadb_isvalidDirName(pMediaDb, direntry->d_name)) { if(mediadb_prepend_dir(pathDb, direntry->d_name, bufDb, sizeof(bufDb)) < 0) { LOG(X_WARNING("Unable to concatenate subdir %s %s"), bufDb, direntry->d_name); } else if(mediadb_prepend_dir(pathMedia, direntry->d_name, bufMedia, sizeof(bufMedia)) < 0) { LOG(X_WARNING("Unable to concatenate subdir %s %s"), bufMedia, direntry->d_name); } else { if(numTnCreated > 1) { file_list_write(pathDb, &fileList); } if((rcsub = iterate_subdirs(pMediaDb, bufDb, bufMedia, level+1, iterIdx)) < 0) { rc = rcsub; } if(g_proc_exit != 0) { break; } } } else if(direntry->d_name[0] != '.') { //fprintf(stderr, "skipping directory '%s'\n", direntry->d_name); } } else if(mediadb_isvalidFileName(pMediaDb, direntry->d_name, 1, 0)) { //fprintf(stdout, "%d media file %s '%s'\n", level, pathMedia, direntry->d_name); if(fileops_stat(pathDb, &st) != 0 && mediadb_mkdirfull(pMediaDb->dbDir, pathDb) < 0) { continue; } if(mediadb_prepend_dir(pathDb, direntry->d_name, bufDb, sizeof(bufDb)) < 0) { LOG(X_ERROR("Unable to concatenate subdir %s %s"), bufDb, direntry->d_name); continue; } else if(mediadb_prepend_dir(pathMedia, direntry->d_name, bufMedia, sizeof(bufMedia)) < 0) { LOG(X_ERROR("Unable to concatenate subdir %s %s"), bufMedia, direntry->d_name); continue; } if(update_fileentry(&fileList, direntry->d_name, bufMedia, &pFileListEntry) > 0) { LOG(X_DEBUG("'%s' changed ('%s')"), direntry->d_name, pFileListEntry->name); fileListChanged = 1; if(pFileListEntry->numTn < 0) { pFileListEntry->numTn = 0; } } if(pMediaDb->avcthumb && pFileListEntry && pFileListEntry->vstr[0] != '\0' && pFileListEntry->numTn != -1) { // // On very first iteration, check all thumbnails // snprintf(tmpfile, sizeof(tmpfile), "%s%s."NOTN_EXT, bufDb, pMediaDb->tn_suffix); if(fileops_stat(tmpfile, &st) == 0 && iterIdx > 0) { // // Ignore thumbnail creation if the .notn file has been // previously created on an error condition // } else // // Update thumbnail info // if((highestTn = createTn(pMediaDb, pFileListEntry, bufMedia, bufDb, pFileListEntry->numTn, 1)) > pFileListEntry->numTn || highestTn == -1) { pFileListEntry->numTn = highestTn; fileListChanged = 1; // Periodically update the db file if(numTnCreated > 10) { file_list_write(pathDb, &fileList); numTnCreated = 0; } } } } else { //fprintf(stdout, "skipping %s '%s'\n", pathMedia, direntry->d_name); } } fileops_CloseDir(pdir); if(g_proc_exit == 0) { if(file_list_removeunmarked(&fileList) || fileListChanged) { LOG(X_DEBUG("Writing file index in %s"), pathDb); //fprintf(stdout, "level:%u\n", level); file_list_write(pathDb, &fileList); } // //Remove thumbnails which are not included in the fileList // clearTn(pathMedia, pathDb, &fileList); } file_list_close(&fileList); return rc; }
int mp4_extractRaw(const MP4_CONTAINER_T *pMp4, const char *outPrfx, float fStart, float fDuration, int overwrite, int extractVid, int extractAud) { BOX_HDLR_T *pBoxHdlr; BOX_T *pBoxTrak; BOX_T *pBox; MP4_TRAK_T mp4Trak; char *outPath = NULL; size_t szPath; FILE_HANDLE fp; int rc = 0; if(!pMp4|| !pMp4->pStream || !pMp4->pStream->cbCheckFd(pMp4->pStream) || !outPrfx) { return -1; } else if(!extractVid && !extractAud) { return -1; } if(!(pBoxTrak = mp4_findBoxInTree(pMp4->proot, *((uint32_t *) "moov"))) || !(pBoxTrak = pBoxTrak->child)) { LOG(X_ERROR("No tracks found in mp4")); return -1; } while(pBoxTrak) { if(pBoxTrak->type != *((uint32_t *) "trak") || !(pBoxHdlr = (BOX_HDLR_T *) mp4_findBoxInTree(pBoxTrak, *((uint32_t *) "hdlr")))) { pBoxTrak = pBoxTrak->pnext; continue; } memset(&mp4Trak, 0, sizeof(MP4_TRAK_T)); mp4Trak.pTrak = pBoxTrak; pBox = fillTrack(&mp4Trak, 0); if(!pBox) { pBoxTrak = pBoxTrak->pnext; continue; } szPath = strlen(outPrfx); outPath = (char *) avc_calloc(1, szPath + 8); memcpy(outPath, outPrfx, szPath); rc = 0; if(pBoxHdlr->handlertype == *((uint32_t *) "soun") || pBoxHdlr->handlertype == *((uint32_t *) "sdsm")) { if(extractAud) { if(mp4_findBoxInTree(pBoxTrak, *((uint32_t *) "mp4a"))) { strncpy(&outPath[szPath], ".aac", 7); } else { LOG(X_WARNING("Unknown audio track %c%c%c%c written as raw output"), ((unsigned char *)&pBox->type)[0], ((unsigned char *)&pBox->type)[1], ((unsigned char *)&pBox->type)[2], ((unsigned char *)&pBox->type)[3]); strncpy(&outPath[szPath], ".araw", 7); } } else { pBox = NULL; } } else if(pBoxHdlr->handlertype == *((uint32_t *) "vide")) { if(extractVid) { if(mp4_findBoxInTree(pBoxTrak, *((uint32_t *) "avc1"))) { strncpy(&outPath[szPath], ".h264", 7); } else if(mp4_findBoxInTree(pBoxTrak, *((uint32_t *) "mp4v"))) { strncpy(&outPath[szPath], ".mpg4", 7); } else { LOG(X_WARNING("Unknown video track %c%c%c%c written as raw output"), ((unsigned char *)&pBox->type)[0], ((unsigned char *)&pBox->type)[1], ((unsigned char *)&pBox->type)[2], ((unsigned char *)&pBox->type)[3]); strncpy(&outPath[szPath], ".vraw", 7); } } else { pBox = NULL; } } else { pBox = NULL; } if(pBox) { if(!overwrite && (fp = fileops_Open(outPath, O_RDONLY)) != FILEOPS_INVALID_FP) { fileops_Close(fp); LOG(X_ERROR("File %s already exists. Will not overwrite."), outPath); free(outPath); pBoxTrak = pBoxTrak->pnext; continue; } if(pBox->type == *((uint32_t *) "avc1")) { rc = mp4_extractAvcVid(pMp4, outPath, fStart, fDuration); } else if(pBox->type == *((uint32_t *) "mp4v")) { rc = mp4_extractMp4Vid(pMp4, outPath, fStart, fDuration); } else if(pBox->type == *((uint32_t *) "mp4a")) { rc = mp4_extractAacAud(pMp4, outPath, fStart, fDuration); } else { rc = extractGenericTrak(pMp4, &mp4Trak, outPath, fStart, fDuration); } if(rc == 0) { LOG(X_INFO("Created %s"), outPath); } free(outPath); } pBoxTrak = pBoxTrak->pnext; } return rc; }
int sdputil_init(SDP_DESCR_T *pSdp, uint8_t payloadType, unsigned int clockRateHz, XC_CODEC_TYPE_T codecType, const char *pDstHost, uint16_t dstPort, uint16_t dstPortRtcp, const SRTP_CTXT_T *pSrtp, const DTLS_CFG_T *pDtlsCfg, const STUN_REQUESTOR_CFG_T *pStunCfg, const SDP_CODEC_PARAM_T *pCodecSpecific, const FRAME_RATE_T *pFps, const VID_ENCODER_FBREQUEST_T *pFbReq) { int rc = 0; char tmp[128]; struct sockaddr_storage connectip; if(!pSdp || payloadType > 0x7f || !pDstHost) { return -1; } memset(&connectip, 0, sizeof(connectip)); if(!net_isipv4(pDstHost) && !net_isipv6(pDstHost)) { // // INADDR_NONE // pDstHost = "0.0.0.0"; } net_getaddress(pDstHost, &connectip); if(INET_ADDR_VALID(connectip) && INET_IS_MULTICAST(connectip)) { pSdp->c.ttl = 64; } else if(INET_ADDR_VALID(connectip) && INET_ADDR_LOCALHOST(connectip)) { pSdp->c.ttl = 0; } else { //connectip.s_addr = net_getlocalip(); // // For remote unicast destinations, leave the remote ip into the 'c=' field // which may be contrary to RFC4566 // //connectip.s_addr = INADDR_ANY; pSdp->c.ttl = 0; } pSdp->c.ip_family = connectip.ss_family; strncpy(pSdp->c.iphost, INET_NTOP(connectip, tmp, sizeof(tmp)), sizeof(pSdp->c.iphost)); // // Only write the RTCP port attribute in the SDP if using a non-default port // if(dstPortRtcp == RTCP_PORT_FROM_RTP(dstPort)) { dstPortRtcp = 0; } if(pFps && pFps->clockHz > 0 && pFps->frameDeltaHz > 0) { memcpy(&pSdp->vid.fps, pFps, sizeof(pSdp->vid.fps)); } switch(codecType) { case XC_CODEC_TYPE_H264: case XC_CODEC_TYPE_MPEG4V: case XC_CODEC_TYPE_H263: case XC_CODEC_TYPE_H263_PLUS: case XC_CODEC_TYPE_VP8: pSdp->vid.common.available = 1; pSdp->vid.common.codecType = codecType; pSdp->vid.common.payloadType = payloadType; pSdp->vid.common.port = dstPort; pSdp->vid.common.portRtcp = dstPortRtcp; pSdp->vid.common.clockHz = clockRateHz; pSdp->vid.common.transType = SDP_TRANS_TYPE_RTP_UDP; if(pDtlsCfg) { if(pDtlsCfg->dtls_srtp) { pSdp->vid.common.transType = SDP_TRANS_TYPE_SRTP_DTLS_UDP; } else { pSdp->vid.common.transType = SDP_TRANS_TYPE_DTLS_UDP; } memcpy(&pSdp->vid.common.fingerprint, &pDtlsCfg->fingerprint, sizeof(pSdp->vid.common.fingerprint)); } if(pSrtp) { pSdp->vid.common.transType = SDP_TRANS_TYPE_SRTP_SDES_UDP; if((rc = sdputil_initsrtp(&pSdp->vid.common.srtp, pSrtp)) < 0) { return rc; } } if(pStunCfg && pStunCfg->bindingRequest) { if(pStunCfg->reqUsername) { strncpy(pSdp->vid.common.ice.ufrag, pStunCfg->reqUsername, STUN_STRING_MAX - 1); } if(pStunCfg->reqPass) { strncpy(pSdp->vid.common.ice.pwd, pStunCfg->reqPass, STUN_STRING_MAX - 1); } } if(codecType == XC_CODEC_TYPE_H264) { strncpy(pSdp->vid.common.encodingName, SDP_RTPMAP_ENCODINGNAME_H264, sizeof(pSdp->vid.common.encodingName)); } else if(codecType == XC_CODEC_TYPE_MPEG4V) { strncpy(pSdp->vid.common.encodingName, SDP_RTPMAP_ENCODINGNAME_MPEG4V, sizeof(pSdp->vid.common.encodingName)); } else if(codecType == XC_CODEC_TYPE_H263) { strncpy(pSdp->vid.common.encodingName, SDP_RTPMAP_ENCODINGNAME_H263, sizeof(pSdp->vid.common.encodingName)); } else if(codecType == XC_CODEC_TYPE_H263_PLUS) { strncpy(pSdp->vid.common.encodingName, SDP_RTPMAP_ENCODINGNAME_H263_PLUS, sizeof(pSdp->vid.common.encodingName)); } else if(codecType == XC_CODEC_TYPE_VP8) { strncpy(pSdp->vid.common.encodingName, SDP_RTPMAP_ENCODINGNAME_VP8, sizeof(pSdp->vid.common.encodingName)); } break; case XC_CODEC_TYPE_AAC: case XC_CODEC_TYPE_AMRNB: case XC_CODEC_TYPE_SILK: case XC_CODEC_TYPE_OPUS: case XC_CODEC_TYPE_G711_MULAW: case XC_CODEC_TYPE_G711_ALAW: pSdp->aud.common.available = 1; pSdp->aud.common.codecType = codecType; pSdp->aud.common.payloadType = payloadType; pSdp->aud.common.port = dstPort; pSdp->aud.common.portRtcp = dstPortRtcp; pSdp->aud.common.clockHz = clockRateHz; pSdp->aud.common.transType = SDP_TRANS_TYPE_RTP_UDP; if(pDtlsCfg) { if(pDtlsCfg->dtls_srtp) { pSdp->aud.common.transType = SDP_TRANS_TYPE_SRTP_DTLS_UDP; } else { pSdp->aud.common.transType = SDP_TRANS_TYPE_DTLS_UDP; } memcpy(&pSdp->aud.common.fingerprint, &pDtlsCfg->fingerprint, sizeof(pSdp->aud.common.fingerprint)); } if(pSrtp) { pSdp->aud.common.transType = SDP_TRANS_TYPE_SRTP_SDES_UDP; if((rc = sdputil_initsrtp(&pSdp->aud.common.srtp, pSrtp)) < 0) { return rc; } } if(pStunCfg && pStunCfg->bindingRequest) { if(pStunCfg->reqUsername) { strncpy(pSdp->aud.common.ice.ufrag, pStunCfg->reqUsername, STUN_STRING_MAX - 1); } if(pStunCfg->reqPass) { strncpy(pSdp->aud.common.ice.pwd, pStunCfg->reqPass, STUN_STRING_MAX - 1); } } if(codecType == XC_CODEC_TYPE_AAC) { strncpy(pSdp->aud.common.encodingName, SDP_RTPMAP_ENCODINGNAME_AAC, sizeof(pSdp->aud.common.encodingName)); } else if(codecType == XC_CODEC_TYPE_AMRNB) { strncpy(pSdp->aud.common.encodingName, SDP_RTPMAP_ENCODINGNAME_AMR, sizeof(pSdp->aud.common.encodingName)); } else if(codecType == XC_CODEC_TYPE_SILK) { strncpy(pSdp->aud.common.encodingName, SDP_RTPMAP_ENCODINGNAME_SILK, sizeof(pSdp->aud.common.encodingName)); } else if(codecType == XC_CODEC_TYPE_OPUS) { strncpy(pSdp->aud.common.encodingName, SDP_RTPMAP_ENCODINGNAME_OPUS, sizeof(pSdp->aud.common.encodingName)); } else if(codecType == XC_CODEC_TYPE_G711_MULAW) { strncpy(pSdp->aud.common.encodingName, SDP_RTPMAP_ENCODINGNAME_PCMU, sizeof(pSdp->aud.common.encodingName)); } else if(codecType == XC_CODEC_TYPE_G711_ALAW) { strncpy(pSdp->aud.common.encodingName, SDP_RTPMAP_ENCODINGNAME_PCMA, sizeof(pSdp->aud.common.encodingName)); } break; case MEDIA_FILE_TYPE_MP2TS: pSdp->vid.common.available = 1; pSdp->vid.common.codecType = MEDIA_FILE_TYPE_MP2TS; pSdp->vid.common.payloadType = payloadType; pSdp->vid.common.port = dstPort; pSdp->vid.common.portRtcp = dstPortRtcp; pSdp->vid.common.clockHz = 90000; if(pSrtp) { pSdp->vid.common.transType = SDP_TRANS_TYPE_SRTP_SDES_UDP; if((rc = sdputil_initsrtp(&pSdp->vid.common.srtp, pSrtp)) < 0) { return rc; } } strncpy(pSdp->vid.common.encodingName, SDP_RTPMAP_ENCODINGNAME_MP2TS, sizeof(pSdp->vid.common.encodingName)); break; default: return -1; } // // Advertise any a=rtcp-fb: SDP flags // if(pSdp->vid.common.available) { if(pFbReq && (pFbReq->firCfg.fir_send_from_decoder || pFbReq->firCfg.fir_send_from_local || pFbReq->firCfg.fir_send_from_remote || pFbReq->firCfg.fir_send_from_capture)) { pSdp->vid.common.rtcpfb.fmtidmin1 = pSdp->vid.common.payloadType + 1; pSdp->vid.common.rtcpfb.flags |= SDP_RTCPFB_TYPE_CCM | SDP_RTCPFB_TYPE_CCM_FIR; } if(pFbReq && pFbReq->nackRtpRetransmit) { pSdp->vid.common.rtcpfb.flags |= SDP_RTCPFB_TYPE_NACK | SDP_RTCPFB_TYPE_NACK_GENERIC; } //pSdp->vid.common.rtcpfb.flags |= SDP_RTCPFB_TYPE_TRRINT; //pSdp->vid.common.rtcpfb.trrIntervalMs |= 30; } // // Codec specific default settings // switch(codecType) { case XC_CODEC_TYPE_H264: if(pCodecSpecific && (pCodecSpecific->flags & SDP_CODEC_PARAM_FLAGS_PKTZMODE)) { switch(pCodecSpecific->u.pktzMode) { case PKTZ_H264_MODE_0: pSdp->vid.u.h264.packetization_mode = 0; break; case PKTZ_H264_MODE_2: LOG(X_WARNING("H.264 NAL Packetization mode 2 not supported. Using mode 1")); case PKTZ_H264_MODE_1: case PKTZ_H264_MODE_NOTSET: default: pSdp->vid.u.h264.packetization_mode = 1; break; } } break; case XC_CODEC_TYPE_MPEG4V: pSdp->vid.u.mpg4v.profile_level_id = 1; break; case XC_CODEC_TYPE_VP8: break; case XC_CODEC_TYPE_AAC: strncpy(pSdp->aud.u.aac.mode, "AAC-hbr", sizeof(pSdp->aud.u.aac.mode)); pSdp->aud.u.aac.sizelength = 13; pSdp->aud.u.aac.indexlength = 3; pSdp->aud.u.aac.indexdeltalength = 3; break; case XC_CODEC_TYPE_AMRNB: pSdp->aud.channels = 1; pSdp->aud.u.amr.octet_align = 1; break; case XC_CODEC_TYPE_SILK: if(pCodecSpecific && (pCodecSpecific->flags & SDP_CODEC_PARAM_FLAGS_CHANNELS)) { pSdp->aud.channels = pCodecSpecific->u.channels; } else { pSdp->aud.channels = 1; } //pSdp->aud.u.silk.dummy = 0; break; case XC_CODEC_TYPE_OPUS: if(pCodecSpecific && (pCodecSpecific->flags & SDP_CODEC_PARAM_FLAGS_CHANNELS)) { pSdp->aud.channels = pCodecSpecific->u.channels;; } else { pSdp->aud.channels = 1; } break; case XC_CODEC_TYPE_G711_MULAW: case XC_CODEC_TYPE_G711_ALAW: pSdp->aud.channels = 1; break; default: break; } return rc; }
static void httplive_mediaproc(void *pArg) { HTTPLIVE_CLIENT_T *pClient = (HTTPLIVE_CLIENT_T *) pArg; int rc = 0; const char *p; const char *puri = NULL; char path[VSX_MAX_PATH_LEN]; pClient->running = 0; LOG(X_DEBUG("Starting HTTPLive client media thread")); path[sizeof(path) - 1] = '\0'; while(rc >= 0 && pClient->pCfg->running == 0 && pClient->insession) { path[0] = '\0'; pthread_mutex_lock(&pClient->mtx); if(pClient->nextidx > pClient->curidx) { LOG(X_WARNING("HTTPLive skipping playlist media chunk index %d - %d"), pClient->curidx, pClient->nextidx - 1); pClient->curidx = pClient->nextidx; //pClient->nextidx++; } if((p = find_path(&pClient->pl, pClient->curidx))) { strncpy(path, p, sizeof(path) - 1); } else { } pthread_mutex_unlock(&pClient->mtx); if(!(puri = get_uri_from_path(pClient, path))) { rc = -1; break; } if(puri && puri[0] != '\0') { // TODO: create httplive retrieval flag forcing new socket... lame if(NETIOSOCK_FD(pClient->netsock) != INVALID_SOCKET && net_issockremotelyclosed(NETIOSOCK_FD(pClient->netsock), 1)) { LOG(X_DEBUG("HTTPLive media socket has been closed")); netio_closesocket(&pClient->netsock); } if(NETIOSOCK_FD(pClient->netsock) == INVALID_SOCKET) { //fprintf(stderr, "----MEDIA GET for '%s' '%s'\n", path, puri); if((rc = httpcli_connect(&pClient->netsock, &pClient->sa, "HTTPLive media thread")) < 0) { break; } } //fprintf(stderr, "may call get_ts puri:'%s', idx:%d, next:%d\n", puri, pClient->curidx, pClient->nextidx); if((rc = get_ts(pClient, puri)) >= 0) { //fprintf(stderr, "HTTPLive ts retrieval '%s' returned %d\n", puri, rc); pthread_mutex_lock(&pClient->mtx); pClient->nextidx++; pClient->curidx = pClient->nextidx; pthread_mutex_unlock(&pClient->mtx); } } // end of if(puri && puri[0] != '\0' ... sleep(1); } netio_closesocket(&pClient->netsock); pClient->insession = 0; pClient->running = -1; LOG(X_DEBUG("HTTPLive media download thread exiting with code %d"), rc); }
static BOX_T *mp4_loadMovieFrag(BOX_T *pPrevMoof, MP4_MOOF_TRAK_T *pMoofTrak, uint32_t trackid) { BOX_TRAF_T *pBoxTraf; BOX_TFHD_T *pBoxTfhd = NULL; int64_t dataOffset = 0; if(!pPrevMoof) { return NULL; } //memset(pMoofTrak, 0, sizeof(MP4_MOOF_TRAK_T)); pMoofTrak->pTfhd = NULL; //fprintf(stderr, "loadMovieFrag called for trackid:%d, pTfhd: 0x%x, mdat:0x%x\n", trackid, pMoofTrak->pTfhd, pMoofTrak->pMdat); do { if(!(pMoofTrak->pMoof = mp4_findBoxNext(pPrevMoof, *((uint32_t *) "moof")))) { //LOG(X_ERROR("Unable to find moof box")); return NULL; } if(!(pMoofTrak->pMdat = mp4_findBoxNext(pMoofTrak->pMdat ? pMoofTrak->pMdat : pPrevMoof, *((uint32_t *) "mdat")))) { LOG(X_ERROR("Unable to find moof mdat box")); return NULL; } if(!(pMoofTrak->pMfhd = (BOX_MFHD_T *) mp4_findBoxChild(pMoofTrak->pMoof, *((uint32_t *) "mfhd")))) { LOG(X_ERROR("Unable to find mfhd box")); return NULL; } // // TODO: A moof w/ audio + video, may contain multiple traf / tfhd boxes // the 'continue' below should first try to look for a next 'traf / tfhd box // if(!(pBoxTraf = mp4_findBoxNext((BOX_T *) pMoofTrak->pMfhd, *((uint32_t *) "traf")))) { LOG(X_ERROR("Unable to find traf box")); return NULL; } if(!(pBoxTfhd = (BOX_TFHD_T *) mp4_findBoxChild(pBoxTraf, *((uint32_t *) "tfhd")))) { LOG(X_ERROR("Unable to find tfhd box")); return NULL; } else if(pBoxTfhd->trackid != trackid) { pPrevMoof = pMoofTrak->pMoof; //fprintf(stderr, "loadMovieFrag skipping moof trackid:%d\n", pBoxTfhd->trackid); continue; } pMoofTrak->pTfhd = pBoxTfhd; if(!(pMoofTrak->pTrun = (BOX_TRUN_T *) mp4_findBoxChild(pBoxTraf, *((uint32_t *) "trun")))) { LOG(X_ERROR("Unable to find trun box")); return NULL; } if((pMoofTrak->pTfhd->flags & TFHD_FLAG_BASE_DATA_OFFSET)) { dataOffset += pMoofTrak->pTfhd->base_data_offset; } if((pMoofTrak->pTrun->flags & TRUN_FLAG_DATA_OFFSET)) { dataOffset += pMoofTrak->pTrun->data_offset; } pMoofTrak->fileOffset = pMoofTrak->pMoof->fileOffset + dataOffset; //fprintf(stderr, "loadMovieFrag called for trackid:%d fileOffset: 0x%llx (0x%llx + 0x%llx)\n", trackid, pMoofTrak->fileOffset, pMoofTrak->pMoof->fileOffset, dataOffset); if((pMoofTrak->fileOffset != pMoofTrak->pMdat->fileOffsetData)) { LOG(X_WARNING("Moof data offset mismatch mdat 0x%"LL64"x + 0x%"LL64"x != 0x%"LL64"x"), pMoofTrak->pMoof->fileOffset, dataOffset, pMoofTrak->pMdat->fileOffsetData); } } while(!pMoofTrak->pTfhd || pMoofTrak->pTfhd->trackid != trackid); //fprintf(stderr, "loadMovieFrag returning...\n"); return pMoofTrak->pMoof; }
static void srv_rtmp_proc(void *pfuncarg) { CLIENT_CONN_T *pConn = (CLIENT_CONN_T *) pfuncarg; STREAMER_CFG_T *pStreamerCfg = NULL; STREAMER_OUTFMT_T *pLiveFmt = NULL; STREAM_STATS_T *pstats = NULL; RTMP_CTXT_T rtmpCtxt; unsigned int numQFull = 0; char buf[SAFE_INET_NTOA_LEN_MAX]; OUTFMT_CFG_T *pOutFmt = NULL; pStreamerCfg = GET_STREAMER_FROM_CONN(pConn); if(pStreamerCfg && pStreamerCfg->action.liveFmts.out[STREAMER_OUTFMT_IDX_RTMP].do_outfmt) { pLiveFmt = &pStreamerCfg->action.liveFmts.out[STREAMER_OUTFMT_IDX_RTMP]; if(pStreamerCfg->pMonitor && pStreamerCfg->pMonitor->active) { if(!(pstats = stream_monitor_createattach(pStreamerCfg->pMonitor, &pConn->sd.sain, STREAM_METHOD_RTMP, STREAM_MONITOR_ABR_NONE))) { } } // // Add a livefmt cb // pOutFmt = outfmt_setCb(pLiveFmt, rtmp_addFrame, &rtmpCtxt, &pLiveFmt->qCfg, pstats, 1, pStreamerCfg->frameThin, &numQFull); } if(pOutFmt) { memset(&rtmpCtxt, 0, sizeof(rtmpCtxt)); rtmp_init(&rtmpCtxt, MAX(pLiveFmt->qCfg.maxPktLen, pLiveFmt->qCfg.growMaxPktLen)); rtmpCtxt.pSd = &pConn->sd; rtmpCtxt.novid = pStreamerCfg->novid; rtmpCtxt.noaud = pStreamerCfg->noaud; rtmpCtxt.av.vid.pStreamerCfg = pStreamerCfg; rtmpCtxt.av.aud.pStreamerCfg = pStreamerCfg; // // Unpause the outfmt callback mechanism now that rtmp_init was called // outfmt_pause(pOutFmt, 0); LOG(X_INFO("Starting rtmp stream[%d] %d/%d to %s:%d"), pOutFmt->cbCtxt.idx, numQFull + 1, pLiveFmt->max, net_inet_ntoa(pConn->sd.sain.sin_addr, buf), ntohs(pConn->sd.sain.sin_port)); rtmp_handle_conn(&rtmpCtxt); LOG(X_INFO("Ending rtmp stream[%d] to %s:%d"), pOutFmt->cbCtxt.idx, net_inet_ntoa(pConn->sd.sain.sin_addr, buf), ntohs(pConn->sd.sain.sin_port)); // // Remove the livefmt cb // outfmt_removeCb(pOutFmt); rtmp_close(&rtmpCtxt); } else { if(pstats) { // // Destroy automatically detaches the stats from the monitor linked list // stream_stats_destroy(&pstats, NULL); } LOG(X_WARNING("No rtmp resource available (max:%d) for %s:%d"), (pLiveFmt ? pLiveFmt->max : 0), net_inet_ntoa(pConn->sd.sain.sin_addr, buf), ntohs(pConn->sd.sain.sin_port)); } netio_closesocket(&pConn->sd.netsocket); LOG(X_DEBUG("RTMP connection thread ended %s:%d"), net_inet_ntoa(pConn->sd.sain.sin_addr, buf), ntohs(pConn->sd.sain.sin_port)); }
static int mp4_readSampleFromTrack_int(MP4_EXTRACT_STATE_INT_T *pState, MP4_MDAT_CONTENT_NODE_T *pContent, uint32_t *pSampleDurationHz) { int sampleSize; int sampleHz; //fprintf(stderr, "readSampleFromTrack tkhdid:%d, mdat: 0x%x, isobmff:%d, tkhd_id:%d, chunk_idx:%d, file:%llu/%llu %lluHz\n", pState->pExtSt->trak.pTkhd->trackid, pState->pExtSt->trak.pMdat, pState->pExtSt->trak.moofTrak.pMp4Root, pContent->tkhd_id, pContent->chunk_idx, pContent->fileOffset, pContent->pStreamIn ? pContent->pStreamIn->size : 0, pContent->playOffsetHz); //fprintf(stderr, "readSampleFromTrack sample[%d], chunk[%d], sampleinchunk[%d]/%d, chunkOffset:%d, stsc[%d]\n", pState->u.tk.idxSample, pState->u.tk.idxChunk, pState->u.tk.idxSampleInChunk, pState->u.tk.samplesInChunk, pState->u.tk.chunkOffset, pState->u.tk.idxStsc); // // If this is a fragmented mp4 look at the moof data // if(pState->pExtSt->trak.moofTrak.pMp4Root) { return readSampleFromISOBMFF(pState, pContent, pSampleDurationHz); } if(!pState->u.tk.isinit || pState->u.tk.idxSampleInChunk >= (unsigned int) pState->u.tk.samplesInChunk) { pState->u.tk.chunkOffset = 0; pState->u.tk.idxSampleInChunk = 0; if(pState->u.tk.isinit) { pState->u.tk.idxChunk++; } if((pState->u.tk.samplesInChunk = getSamplesInChunk(pState->pExtSt->trak.pStsc, pState->u.tk.idxChunk, &pState->u.tk.idxStsc)) < 0) { if(pState->u.tk.idxChunk == 0 && pState->u.tk.idxStsc == 0) { pState->pExtSt->atEndOfTrack = 1; } else { LOG(X_ERROR("Invalid samples in chunks for chunk[%d], stsc[%d]"), pState->u.tk.idxChunk, pState->u.tk.idxStsc); } return -1; } } //fprintf(stderr, "IDXCHUNK:%d/%d, IDXSTSC:%d\n", pState->u.tk.idxChunk, pState->pExtSt->trak.pStco->entrycnt, pState->u.tk.idxStsc); if(pState->u.tk.idxChunk >= pState->pExtSt->trak.pStco->entrycnt) { // // Reached the end of the track // pState->pExtSt->atEndOfTrack = 1; LOG(X_WARNING("sample table chunk sample index end[%d/%d], stsc[%d]"), pState->u.tk.idxChunk, pState->pExtSt->trak.pStco->entrycnt, pState->u.tk.idxStsc); return -1; } if((sampleSize = getSamplesSize(pState->pExtSt->trak.pStsz, pState->u.tk.idxSample)) < 0) { LOG(X_ERROR("Invalid sample size for sample[%d], chunk[%d] (sample:%u)"), pState->u.tk.idxSampleInChunk, pState->u.tk.idxChunk, pState->u.tk.idxSample); return -1; } if((sampleHz = getSampleDuration(pState->pExtSt->trak.pStts, &pState->u.tk.idxSampleInStts, &pState->u.tk.idxStts)) < 0) { return -1; } if(isSyncSample(pState->pExtSt->trak.pStss, &pState->u.tk.idxStss, pState->u.tk.idxSample)) { pContent->flags = MP4_MDAT_CONTENT_FLAG_SYNCSAMPLE; } else { pContent->flags = 0; } pContent->sizeRd = sampleSize; pContent->chunk_idx = pState->u.tk.idxChunk; pContent->fileOffset = pState->pExtSt->trak.pStco->pSamples[pState->u.tk.idxChunk] + pState->u.tk.chunkOffset; //fprintf(stdout, "chunk:%d smpl:%u(%u)%d chk in file:0x%x +0x%x (0x%llx)\n", pState->u.tk.idxChunk, pState->u.tk.idxSampleInChunk,pState->u.tk.idxSample,pContent->flags,pState->pExtSt->trak.pStco->pSamples[pState->u.tk.idxChunk],pState->u.tk.chunkOffset, pContent->fileOffset); if(pSampleDurationHz) { *pSampleDurationHz = sampleHz; } pState->u.tk.idxSample++; pState->u.tk.idxSampleInChunk++; pState->u.tk.chunkOffset += sampleSize; if(pState->u.tk.isinit == 0) { pState->u.tk.isinit = 1; } return 0; }
static void srvlisten_http_proc(void *pArg) { SRV_LISTENER_CFG_T *pListenCfg = (SRV_LISTENER_CFG_T *) pArg; NETIO_SOCK_T netsocksrv; struct sockaddr_in sa; CLIENT_CONN_T *pConn; unsigned int tsMax = 0; unsigned int flvMax = 0; unsigned int mkvMax = 0; /* int haveMkvAuth = 0; int haveFlvAuth = 0; int haveTsliveAuth = 0; int haveHttpliveAuth = 0; int haveMoofAuth = 0; int haveConfigAuth = 0; int havePipAuth = 0; int haveStatusAuth = 0; int haveRootAuth = 0; */ int haveAuth = 0; int rc = 0; char buf[SAFE_INET_NTOA_LEN_MAX]; logutil_tid_add(pthread_self(), pListenCfg->tid_tag); if((pListenCfg->urlCapabilities & (URL_CAP_TSLIVE | URL_CAP_TSHTTPLIVE | URL_CAP_FLVLIVE | URL_CAP_MKVLIVE | URL_CAP_LIVE | URL_CAP_STATUS | URL_CAP_MOOFLIVE | URL_CAP_PIP | URL_CAP_CONFIG | URL_CAP_BROADCAST)) == 0) { LOG(X_WARNING("http listener exiting because no capabilities enabled on %s:%d"), inet_ntoa(pListenCfg->sain.sin_addr), ntohs(pListenCfg->sain.sin_port)); logutil_tid_remove(pthread_self()); return; } memset(&sa, 0, sizeof(sa)); memset(&netsocksrv, 0, sizeof(netsocksrv)); sa.sin_family = PF_INET; sa.sin_addr = pListenCfg->sain.sin_addr; sa.sin_port = pListenCfg->sain.sin_port; netsocksrv.flags = pListenCfg->netflags; if((NETIOSOCK_FD(netsocksrv) = net_listen(&sa, 5)) == INVALID_SOCKET) { logutil_tid_remove(pthread_self()); return; } pthread_mutex_lock(&pListenCfg->mtx); pListenCfg->pnetsockSrv = &netsocksrv; pthread_mutex_unlock(&pListenCfg->mtx); if(pListenCfg->pAuthStore && IS_AUTH_CREDENTIALS_SET(pListenCfg->pAuthStore)) { haveAuth = 1; } pConn = (CLIENT_CONN_T *) pListenCfg->pConnPool->pElements; if(pConn) { tsMax = pConn->pStreamerCfg0->liveQs[0].max; flvMax = pConn->pStreamerCfg0->action.liveFmts.out[STREAMER_OUTFMT_IDX_FLV].max; mkvMax = pConn->pStreamerCfg0->action.liveFmts.out[STREAMER_OUTFMT_IDX_MKV].max; /* if(IS_AUTH_CREDENTIALS_SET(&pConn->pStreamerCfg0->creds[STREAMER_AUTH_IDX_FLV].stores[pListenCfg->idxCfg])) { haveFlvAuth = 1; } if(IS_AUTH_CREDENTIALS_SET(&pConn->pStreamerCfg0->creds[STREAMER_AUTH_IDX_MKV].stores[pListenCfg->idxCfg])) { haveMkvAuth = 1; } if(IS_AUTH_CREDENTIALS_SET(&pConn->pStreamerCfg0->creds[STREAMER_AUTH_IDX_TSLIVE].stores[pListenCfg->idxCfg])) { haveTsliveAuth = 1; } if(IS_AUTH_CREDENTIALS_SET(&pConn->pStreamerCfg0->creds[STREAMER_AUTH_IDX_HTTPLIVE].stores[pListenCfg->idxCfg])) { haveHttpliveAuth = 1; } if(IS_AUTH_CREDENTIALS_SET(&pConn->pStreamerCfg0->creds[STREAMER_AUTH_IDX_MOOFLIVE].stores[pListenCfg->idxCfg])) { haveMoofAuth = 1; } if(IS_AUTH_CREDENTIALS_SET(&pConn->pStreamerCfg0->creds[STREAMER_AUTH_IDX_CONFIG].stores[pListenCfg->idxCfg])) { haveConfigAuth = 1; } if(IS_AUTH_CREDENTIALS_SET(&pConn->pStreamerCfg0->creds[STREAMER_AUTH_IDX_PIP].stores[pListenCfg->idxCfg])) { havePipAuth = 1; } if(IS_AUTH_CREDENTIALS_SET(&pConn->pStreamerCfg0->creds[STREAMER_AUTH_IDX_STATUS].stores[pListenCfg->idxCfg])) { haveStatusAuth = 1; } if(IS_AUTH_CREDENTIALS_SET(&pConn->pStreamerCfg0->creds[STREAMER_AUTH_IDX_ROOT].stores[pListenCfg->idxCfg])) { haveRootAuth = 1; } */ //if(IS_AUTH_CREDENTIALS_SET(&pConn->pStreamerCfg0->creds.auths[STREAMER_AUTH_IDX_ROOT])) { // LOG(X_DEBUG("LIVE/ROOT AUTH ON")); //} } if((pListenCfg->urlCapabilities & URL_CAP_ROOTHTML)) { LOG(X_INFO("broadcast interface available at "URL_HTTP_FMT_STR"%s%s%s max:%d"), URL_HTTP_FMT_ARGS2(pListenCfg, net_inet_ntoa(sa.sin_addr, buf)), VSX_ROOT_URL, //(haveRootAuth ? " (Using auth)" : ""), (haveAuth ? " (Using auth)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), pListenCfg->max); } if((pListenCfg->urlCapabilities & URL_CAP_LIVE)) { LOG(X_INFO("live available at "URL_HTTP_FMT_STR"%s%s%s max:%d"), URL_HTTP_FMT_ARGS2(pListenCfg, net_inet_ntoa(sa.sin_addr, buf)), VSX_LIVE_URL, //(haveRootAuth ? " (Using auth)" : ""), (haveAuth ? " (Using auth)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), pListenCfg->max); } if((pListenCfg->urlCapabilities & URL_CAP_TSLIVE)) { LOG(X_INFO("tslive available at "URL_HTTP_FMT_STR"%s%s%s max:%d"), URL_HTTP_FMT_ARGS2(pListenCfg, net_inet_ntoa(sa.sin_addr, buf)), VSX_TSLIVE_URL, //(haveTsliveAuth ? " (Using auth)" : ""), (haveAuth ? " (Using auth)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), tsMax); } if((pListenCfg->urlCapabilities & URL_CAP_TSHTTPLIVE)) { LOG(X_INFO("httplive available at "URL_HTTP_FMT_STR"%s%s%s max:%d"), URL_HTTP_FMT_ARGS2(pListenCfg, net_inet_ntoa(sa.sin_addr, buf)), VSX_HTTPLIVE_URL, //(haveHttpliveAuth ? " (Using auth)" : ""), (haveAuth ? " (Using auth)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), pListenCfg->max); } if((pListenCfg->urlCapabilities & URL_CAP_FLVLIVE)) { LOG(X_INFO("flvlive available at "URL_HTTP_FMT_STR"%s%s%s max:%d"), URL_HTTP_FMT_ARGS2(pListenCfg, net_inet_ntoa(sa.sin_addr, buf)), VSX_FLVLIVE_URL, //(haveFlvAuth ? " (Using auth)" : ""), (haveAuth ? " (Using auth)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), flvMax); } if((pListenCfg->urlCapabilities & URL_CAP_MKVLIVE)) { LOG(X_INFO("mkvlive available at "URL_HTTP_FMT_STR"%s%s%s max:%d"), URL_HTTP_FMT_ARGS2(pListenCfg, net_inet_ntoa(sa.sin_addr, buf)), VSX_MKVLIVE_URL, //(haveMkvAuth ? " (Using auth)" : ""), (haveAuth ? " (Using auth)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), mkvMax); } if((pListenCfg->urlCapabilities & URL_CAP_MOOFLIVE)) { LOG(X_INFO("dash available at "URL_HTTP_FMT_STR"%s%s%s max:%d"), URL_HTTP_FMT_ARGS2(pListenCfg, net_inet_ntoa(sa.sin_addr, buf)), VSX_DASH_URL, // (haveMoofAuth ? " (Using auth)" : ""), (haveAuth ? " (Using auth)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), pListenCfg->max); } if((pListenCfg->urlCapabilities & URL_CAP_STATUS)) { LOG(X_INFO("status available at "URL_HTTP_FMT_STR"%s%s%s"), URL_HTTP_FMT_ARGS2(pListenCfg, net_inet_ntoa(sa.sin_addr, buf)), VSX_STATUS_URL, //(haveStatusAuth ? " (Using auth)" : ""), (haveAuth ? " (Using auth)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : "")); } if((pListenCfg->urlCapabilities & URL_CAP_PIP)) { LOG(X_INFO("pip available at "URL_HTTP_FMT_STR"%s%s%s"), URL_HTTP_FMT_ARGS2(pListenCfg, net_inet_ntoa(sa.sin_addr, buf)), VSX_PIP_URL, //(havePipAuth ? " (Using auth)" : ""), (haveAuth ? " (Using auth)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : "")); } if((pListenCfg->urlCapabilities & URL_CAP_CONFIG)) { LOG(X_INFO("config available at "URL_HTTP_FMT_STR"%s%s%s"), URL_HTTP_FMT_ARGS2(pListenCfg, net_inet_ntoa(sa.sin_addr, buf)), VSX_CONFIG_URL, //(haveConfigAuth ? " (Using auth)" : ""), (haveAuth ? " (Using auth)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : "")); } //fprintf(stderr, "LISTENER THREAD %s %s:%d active:%d cap: 0x%x\n", pListenCfg->netflags & NETIO_FLAG_SSL_TLS ? "SSL" : "", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port), pListenCfg->active, pListenCfg->urlCapabilities); // // Service any client connections on the live listening port // rc = srvlisten_loop(pListenCfg, srv_cmd_proc); pthread_mutex_lock(&pListenCfg->mtx); pListenCfg->pnetsockSrv = NULL; netio_closesocket(&netsocksrv); pthread_mutex_unlock(&pListenCfg->mtx); LOG(X_WARNING("HTTP listener thread exiting with code: %d"), rc); logutil_tid_remove(pthread_self()); return; }
int srvlisten_loop(SRV_LISTENER_CFG_T *pListenCfg, void *thread_func) { int salen; THREAD_FUNC_WRAPPER_ARG_T wrapArg; CLIENT_CONN_T *pConn; SOCKET_DESCR_T sdclient; int rc = -1; const char *s; //pthread_cond_t cond; pthread_mutex_t mtx; TIME_VAL tv0, tv1; char tmp[128]; #if defined(__APPLE__) int sockopt = 0; #endif // __APPLE__ if(!pListenCfg || !pListenCfg->pnetsockSrv || !pListenCfg->pConnPool || !thread_func || pListenCfg->pConnPool->numElements <= 0) { return -1; } //memset(&saSrv, 0, sizeof(saSrv)); memset(&sdclient.netsocket, 0, sizeof(sdclient.netsocket)); //salen = sizeof(saSrv); //if(getsockname(pListenCfg->pnetsockSrv->sock, (struct sockaddr *) &saSrv, (socklen_t *) &salen) != 0) { // LOG(X_ERROR("getsockopt failed on server socket")); //} pthread_mutex_init(&mtx, NULL); //pthread_cond_init(&cond, NULL); while(!g_proc_exit) { salen = sizeof(sdclient.sa); if((NETIOSOCK_FD(sdclient.netsocket) = accept(PNETIOSOCK_FD(pListenCfg->pnetsockSrv), (struct sockaddr *) &sdclient.sa, (socklen_t *) &salen)) == INVALID_SOCKET) { if(!g_proc_exit) { LOG(X_ERROR("%saccept failed on %s:%d"), ((pListenCfg->pnetsockSrv->flags & NETIO_FLAG_SSL_TLS) ? "SSL " : ""), //inet_ntoa(saSrv.sin_addr), ntohs(saSrv.sin_port)); FORMAT_NETADDR(pListenCfg->sa, tmp, sizeof(tmp)), ntohs(INET_PORT(pListenCfg->sa))); } break; } if(g_proc_exit) { break; } sdclient.netsocket.flags = pListenCfg->pnetsockSrv->flags; // // Find an available client thread to process the client request // if((pConn = (CLIENT_CONN_T *) pool_get(pListenCfg->pConnPool)) == NULL) { LOG(X_WARNING("No available connection for %s:%d (max:%d) on port %d"), FORMAT_NETADDR(sdclient.sa, tmp, sizeof(tmp)), ntohs(INET_PORT(sdclient.sa)), pListenCfg->pConnPool->numElements, ntohs(INET_PORT(pListenCfg->sa))); netio_closesocket(&sdclient.netsocket); continue; } #if defined(__APPLE__) sockopt = 1; if(setsockopt(NETIOSOCK_FD(sdclient.netsocket), SOL_SOCKET, SO_NOSIGPIPE, (char*) &sockopt, sizeof(sockopt)) != 0) { LOG(X_ERROR("Failed to set SO_NOSIGPIPE")); } #endif // __APPLE__ //pConn->psrvsaListen = &saSrv; //memcpy(&pConn->srvsaListen, &saSrv, sizeof(pConn->srvsaListen)); LOG(X_DEBUG("Accepted connection on port %d from %s:%d"), htons(INET_PORT(pListenCfg->sa)), FORMAT_NETADDR(sdclient.sa, tmp, sizeof(tmp)), htons(INET_PORT(sdclient.sa))); pthread_attr_init(&pConn->attr); pthread_attr_setdetachstate(&pConn->attr, PTHREAD_CREATE_DETACHED); memset(&pConn->sd.netsocket, 0, sizeof(pConn->sd.netsocket)); NETIO_SET(pConn->sd.netsocket, sdclient.netsocket); memcpy(&pConn->sd.sa, &sdclient.sa, INET_SIZE(sdclient)); pConn->pListenCfg = pListenCfg; NETIOSOCK_FD(sdclient.netsocket) = INVALID_SOCKET; wrapArg.thread_func = thread_func; wrapArg.pConnPool = pListenCfg->pConnPool; wrapArg.pConn = pConn; wrapArg.flags = 1; wrapArg.tid_tag[0] = '\0'; if((s = logutil_tid_lookup(pthread_self(), 0)) && s[0] != '\0') { snprintf(wrapArg.tid_tag, sizeof(wrapArg.tid_tag), "%s-%u", s, pConn->pool.id); } //wrapArg.pcond = &cond; //fprintf(stderr, "%d CALLING wrap: 0x%x pConn:0x%x\n", pthread_self(), &wrapArg, wrapArg.pConn); if((rc = pthread_create(&pConn->ptd, &pConn->attr, (void *) thread_func_wrapper, (void *) &wrapArg)) != 0) { LOG(X_ERROR("Unable to create connection handler thread on port %d from %s:%d (%d %s)"), htons(INET_PORT(pListenCfg->sa)), FORMAT_NETADDR(sdclient.sa, tmp, sizeof(tmp)), htons(INET_PORT(sdclient.sa)), rc, strerror(rc)); netio_closesocket(&pConn->sd.netsocket); pool_return(pListenCfg->pConnPool, &pConn->pool); wrapArg.flags = 0; //pthread_cond_broadcast(&cond); break; } pthread_attr_destroy(&pConn->attr); // // be careful not to reuse the same wrapArg instance // since the stack variable arguments could get // overwritten by the next loop iteration, before the thread proc is // invoked // //fprintf(stderr, "wait start\n"); tv0 = timer_GetTime(); //if(wrapArg.flags == 1) { // // It seems that calling pthread_cond_wait here to check if the thread creation is // complete is not completely reliable and portable, so we do the lame way // of sleeping and polling. // //pthread_cond_wait(&cond, &mtx); while(wrapArg.flags == 1) { usleep(100); if(((tv1 = timer_GetTime()) - tv0) / TIME_VAL_MS > 1000) { LOG(X_WARNING("Abandoning wait for connection thread start on port %d from %s:%d"), htons(INET_PORT(pListenCfg->sa)), FORMAT_NETADDR(pListenCfg->sa, tmp, sizeof(tmp)), ntohs(INET_PORT(pListenCfg->sa))); break; } } //fprintf(stderr, "THREAD STARTED AFTER %lld ns\n", (timer_GetTime() - tv0)); //} //fprintf(stderr, "wait done\n"); //while(wrapArg.flag == 1) { // usleep(10000); //} } //pthread_cond_destroy(&cond); pthread_mutex_destroy(&mtx); return rc; }
int srv_rtmp_proc(CLIENT_CONN_T *pConn, const unsigned char *prebuf, unsigned int prebufsz, int istunneled) { int rc = 0; STREAMER_CFG_T *pStreamerCfg = NULL; STREAMER_OUTFMT_T *pLiveFmt = NULL; STREAM_STATS_T *pstats = NULL; RTMP_CTXT_T rtmpCtxt; unsigned int numQFull = 0; char tmps[2][128]; OUTFMT_CFG_T *pOutFmt = NULL; if(!pConn) { return -1; } if(!HAVE_URL_CAP_RTMP(pConn->pListenCfg->urlCapabilities)) { LOG(X_ERROR("Listener %s:%d not enabled for rtmp%s%s stream to %s:%d"), FORMAT_NETADDR(pConn->pListenCfg->sa, tmps[1], sizeof(tmps[1])), ntohs(INET_PORT(pConn->pListenCfg->sa)), istunneled > 0 ? "t" : "", (pConn->sd.netsocket.flags & NETIO_FLAG_SSL_TLS) ? "s" : "", FORMAT_NETADDR(pConn->sd.sa, tmps[0], sizeof(tmps[0])), ntohs(INET_PORT(pConn->sd.sa))); return -1; } pStreamerCfg = GET_STREAMER_FROM_CONN(pConn); if(pStreamerCfg && pStreamerCfg->action.liveFmts.out[STREAMER_OUTFMT_IDX_RTMP].do_outfmt) { pLiveFmt = &pStreamerCfg->action.liveFmts.out[STREAMER_OUTFMT_IDX_RTMP]; if(pStreamerCfg->pMonitor && pStreamerCfg->pMonitor->active) { if(!(pstats = stream_monitor_createattach(pStreamerCfg->pMonitor, (const struct sockaddr *) &pConn->sd.sa, STREAM_METHOD_RTMP, STREAM_MONITOR_ABR_NONE))) { } } // // Add a livefmt cb // pOutFmt = outfmt_setCb(pLiveFmt, rtmp_addFrame, &rtmpCtxt, &pLiveFmt->qCfg, pstats, 1, pStreamerCfg->frameThin, &numQFull); } if(pOutFmt) { memset(&rtmpCtxt, 0, sizeof(rtmpCtxt)); rtmp_init(&rtmpCtxt, MAX(pLiveFmt->qCfg.maxPktLen, pLiveFmt->qCfg.growMaxPktLen)); rtmpCtxt.pSd = &pConn->sd; rtmpCtxt.novid = pStreamerCfg->novid; rtmpCtxt.noaud = pStreamerCfg->noaud; rtmpCtxt.av.vid.pStreamerCfg = pStreamerCfg; rtmpCtxt.av.aud.pStreamerCfg = pStreamerCfg; rtmpCtxt.pAuthTokenId = pConn->pListenCfg->pAuthTokenId; if(pstats) { rtmpCtxt.pStreamMethod = &pstats->method; } rtmpCtxt.pOutFmt = pOutFmt; rtmpCtxt.prebufdata = (unsigned char *) prebuf; rtmpCtxt.prebufsz = prebufsz; if(!(pConn->pListenCfg->urlCapabilities & URL_CAP_RTMPLIVE)) { rtmpCtxt.donotunnel = 1; } if((pConn->pListenCfg->urlCapabilities & URL_CAP_RTMPTLIVE)) { rtmpCtxt.dohttptunnel = 1; } // // Unpause the outfmt callback mechanism now that rtmp_init was called // outfmt_pause(pOutFmt, 0); LOG(X_INFO("Starting rtmp%s%s stream[%d] %d/%d to %s:%d"), istunneled > 0 ? "t" : "", (pConn->sd.netsocket.flags & NETIO_FLAG_SSL_TLS) ? "s" : "", pOutFmt->cbCtxt.idx, numQFull + 1, pLiveFmt->max, FORMAT_NETADDR(pConn->sd.sa, tmps[0], sizeof(tmps[0])), ntohs(INET_PORT(pConn->sd.sa))); rtmp_handle_conn(&rtmpCtxt); LOG(X_INFO("Ending rtmp%s%s stream[%d] to %s:%d"), istunneled > 0 ? "t" : "", (pConn->sd.netsocket.flags & NETIO_FLAG_SSL_TLS) ? "s" : "", pOutFmt->cbCtxt.idx, FORMAT_NETADDR(pConn->sd.sa, tmps[0], sizeof(tmps[0])), ntohs(INET_PORT(pConn->sd.sa))); // // Remove the livefmt cb // outfmt_removeCb(pOutFmt); rtmp_close(&rtmpCtxt); } else { if(pstats) { // // Destroy automatically detaches the stats from the monitor linked list // stream_stats_destroy(&pstats, NULL); } LOG(X_WARNING("No rtmp resource available (max:%d) for %s:%d"), (pLiveFmt ? pLiveFmt->max : 0), FORMAT_NETADDR(pConn->sd.sa, tmps[0], sizeof(tmps[0])), ntohs(INET_PORT(pConn->sd.sa))); rc = -1; } netio_closesocket(&pConn->sd.netsocket); LOG(X_DEBUG("RTMP connection thread ended %s:%d"), FORMAT_NETADDR(pConn->sd.sa, tmps[0], sizeof(tmps[0])), ntohs(INET_PORT(pConn->sd.sa))); return rc; }
static int h264_decode_NAL_int(H264_DECODER_CTXT_T *pCtxt, BIT_STREAM_T *pStream) { int rc = H264_RC_OK; int nalType = pStream->buf[pStream->byteIdx]; if(nalType & 0x80) { LOG(X_ERROR("NAL forbidden bit set!")); return -1; } H264_STREAM_INCR_BYTE(pStream); pCtxt->lastSliceType = H264_SLICE_TYPE_UNKNOWN; switch(nalType & NAL_TYPE_MASK) { case NAL_TYPE_SEQ_PARAM: //fprintf(stderr, "SPS:\n");avc_dumpHex(stderr, &pStream->buf[pStream->byteIdx], 32, 1); rc = h264_decodeSPS_int(pCtxt, pStream); pCtxt->lastSliceType = H264_SLICE_TYPE_SPS; H264_STREAM_ENDOF_BYTE(pStream); break; case NAL_TYPE_PIC_PARAM: //fprintf(stderr, "PPS:\n");avc_dumpHex(stderr, &pStream->buf[pStream->byteIdx], 32, 1); // Prevent attempting to decode PPS w/o having received SPS context if(!(pCtxt->flag & H264_DECODER_CTXT_HAVE_SPS)) { return H264_RC_IGNORED; } rc = h264_decodePPS_int(pCtxt, pStream); pCtxt->lastSliceType = H264_SLICE_TYPE_PPS; H264_STREAM_ENDOF_BYTE(pStream); break; case NAL_TYPE_SEI: //rc = h264_decodeSEI_int(pCtxt, pStream); rc = H264_RC_OK; pCtxt->lastSliceType = H264_SLICE_TYPE_SEI; H264_STREAM_ENDOF_BYTE(pStream); break; case NAL_TYPE_SLICE: case NAL_TYPE_IDR: // Prevent attempting to decode VCL data w/o having received SPS & PPS context if(!(pCtxt->flag & (H264_DECODER_CTXT_HAVE_SPS | H264_DECODER_CTXT_HAVE_PPS))) { return H264_RC_IGNORED; } rc = h264_decodeSlice(pCtxt, pStream, (nalType & NAL_TYPE_MASK)); H264_STREAM_ENDOF_BYTE(pStream); break; case NAL_TYPE_ACCESS_UNIT_DELIM: // quietly ignore break; default: LOG(X_WARNING("No handler for NAL type: %d"), nalType); rc = H264_RC_ERR; break; } return rc; }
static void srvlisten_media_proc(void *pArg) { SRV_LISTENER_CFG_T *pListenCfg = (SRV_LISTENER_CFG_T *) pArg; NETIO_SOCK_T netsocksrv; struct sockaddr_storage sa; CLIENT_CONN_T *pConnTmp; char tmp[128]; char bufses[32]; const int backlog = NET_BACKLOG_DEFAULT; unsigned int tsMax = 0; unsigned int flvMax = 0; unsigned int mkvMax = 0; unsigned int rtmpMax = 0; unsigned int rtspMax = 0; int haveAuth = 0; int haveRtmpAuth = 0; int haveRtspAuth = 0; int haveToken = 0; int rc = 0; logutil_tid_add(pthread_self(), pListenCfg->tid_tag); if((pListenCfg->urlCapabilities & (URL_CAP_TSLIVE | URL_CAP_TSHTTPLIVE | URL_CAP_FLVLIVE | URL_CAP_MKVLIVE | URL_CAP_LIVE | URL_CAP_STATUS | URL_CAP_MOOFLIVE | URL_CAP_PIP | URL_CAP_CONFIG | URL_CAP_BROADCAST | URL_CAP_RTMPLIVE | URL_CAP_RTMPTLIVE | URL_CAP_RTSPLIVE)) == 0) { LOG(X_WARNING("Server listener exiting because no capabilities enabled on %s:%d"), FORMAT_NETADDR(pListenCfg->sa, tmp, sizeof(tmp)), ntohs(INET_PORT(pListenCfg->sa))); logutil_tid_remove(pthread_self()); return; } else if(!(pConnTmp = (CLIENT_CONN_T *) pListenCfg->pConnPool->pElements)) { return; } memset(&netsocksrv, 0, sizeof(netsocksrv)); memcpy(&sa, &pListenCfg->sa, sizeof(pListenCfg->sa)); netsocksrv.flags = pListenCfg->netflags; if((NETIOSOCK_FD(netsocksrv) = net_listen((const struct sockaddr *) &sa, backlog)) == INVALID_SOCKET) { logutil_tid_remove(pthread_self()); return; } pthread_mutex_lock(&pListenCfg->mtx); pListenCfg->pnetsockSrv = &netsocksrv; pthread_mutex_unlock(&pListenCfg->mtx); if(pListenCfg->pAuthStore && IS_AUTH_CREDENTIALS_SET(pListenCfg->pAuthStore)) { haveAuth = 1; } if(pListenCfg->pAuthTokenId && pListenCfg->pAuthTokenId[0] != '\0') { haveToken = 1; } tsMax = pConnTmp->pStreamerCfg0->liveQs[0].max; flvMax = pConnTmp->pStreamerCfg0->action.liveFmts.out[STREAMER_OUTFMT_IDX_FLV].max; mkvMax = pConnTmp->pStreamerCfg0->action.liveFmts.out[STREAMER_OUTFMT_IDX_MKV].max; rtmpMax = pConnTmp->pStreamerCfg0->action.liveFmts.out[STREAMER_OUTFMT_IDX_RTMP].max; rtspMax = pConnTmp->pStreamerCfg0->pRtspSessions->max; if(HAVE_URL_CAP_RTMP(pListenCfg->urlCapabilities)) { if(IS_AUTH_CREDENTIALS_SET(&pConnTmp->pStreamerCfg0->creds[STREAMER_AUTH_IDX_RTMP].stores[pListenCfg->idxCfg])) { // // RTMP server streaming credentials not implemented // //haveRtmpAuth = 1; } LOG(X_INFO("rtmp %s%s available at "URL_RTMP_FMT_STR"%s max:%d"), ((pListenCfg->netflags & NETIO_FLAG_PLAINTEXT) && (pListenCfg->netflags & NETIO_FLAG_SSL_TLS) ) ? "(SSL) " : ((pListenCfg->netflags & NETIO_FLAG_SSL_TLS) ? "(SSL only) " : ""), ((pListenCfg->urlCapabilities & URL_CAP_RTMPTLIVE) && !(pListenCfg->urlCapabilities & URL_CAP_RTMPLIVE) ? "(tunneled only) " : ((pListenCfg->urlCapabilities & URL_CAP_RTMPTLIVE) ? "(tunneled) " : "")), URL_PROTO_FMT2_ARGS( (pListenCfg->netflags & NETIO_FLAG_SSL_TLS), FORMAT_NETADDR(sa, tmp, sizeof(tmp))), ntohs(INET_PORT(sa)), (haveRtmpAuth ? " (Using auth)" : ""), rtmpMax); } if(pListenCfg->urlCapabilities & URL_CAP_RTSPLIVE) { if(IS_AUTH_CREDENTIALS_SET(&pConnTmp->pStreamerCfg0->creds[STREAMER_AUTH_IDX_RTSP].stores[pListenCfg->idxCfg])) { haveRtspAuth = 1; } if(pListenCfg->pCfg->prtspsessiontimeout && *pListenCfg->pCfg->prtspsessiontimeout != 0) { snprintf(bufses, sizeof(bufses), ", timeout:%u sec", *pListenCfg->pCfg->prtspsessiontimeout); } else { bufses[0] = '\0'; } LOG(X_INFO("rtsp %s available at "URL_RTSP_FMT_STR"%s max:%d%s"), ((pListenCfg->netflags & NETIO_FLAG_PLAINTEXT) && (pListenCfg->netflags & NETIO_FLAG_SSL_TLS) ) ? "(SSL) " : ((pListenCfg->netflags & NETIO_FLAG_SSL_TLS) ? "(SSL only) " : ""), URL_PROTO_FMT2_ARGS( (pListenCfg->netflags & NETIO_FLAG_SSL_TLS), FORMAT_NETADDR(sa, tmp, sizeof(tmp))), ntohs(INET_PORT(sa)), (haveRtspAuth ? " (Using auth)" : ""), rtspMax, bufses); } if((pListenCfg->urlCapabilities & URL_CAP_ROOTHTML)) { LOG(X_INFO("broadcast interface available at "URL_HTTP_FMT_STR"%s%s%s max:%d"), URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_ROOT_URL, (haveAuth ? " (Using auth)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), pListenCfg->max); } if((pListenCfg->urlCapabilities & URL_CAP_LIVE)) { LOG(X_INFO("live available at "URL_HTTP_FMT_STR"%s%s%s%s max:%d"), URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_LIVE_URL, (haveAuth ? " (Using auth)" : ""), (haveToken ? " (Using token)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), pListenCfg->max); } if((pListenCfg->urlCapabilities & URL_CAP_TSLIVE)) { LOG(X_INFO("tslive available at "URL_HTTP_FMT_STR"%s%s%s%s max:%d"), URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_TSLIVE_URL, (haveAuth ? " (Using auth)" : ""), (haveToken ? " (Using token)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), tsMax); } if((pListenCfg->urlCapabilities & URL_CAP_TSHTTPLIVE)) { LOG(X_INFO("httplive available at "URL_HTTP_FMT_STR"%s%s%s%s max:%d"), URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_HTTPLIVE_URL, (haveAuth ? " (Using auth)" : ""), (haveToken ? " (Using token)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), pListenCfg->max); } if((pListenCfg->urlCapabilities & URL_CAP_FLVLIVE)) { LOG(X_INFO("flvlive available at "URL_HTTP_FMT_STR"%s%s%s%s max:%d"), URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_FLVLIVE_URL, (haveAuth ? " (Using auth)" : ""), (haveToken ? " (Using token)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), flvMax); } if((pListenCfg->urlCapabilities & URL_CAP_MKVLIVE)) { LOG(X_INFO("mkvlive available at "URL_HTTP_FMT_STR"%s%s%s%s max:%d"), URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_MKVLIVE_URL, (haveAuth ? " (Using auth)" : ""), (haveToken ? " (Using token)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), mkvMax); } if((pListenCfg->urlCapabilities & URL_CAP_MOOFLIVE)) { LOG(X_INFO("dash available at "URL_HTTP_FMT_STR"%s%s%s%s max:%d"), URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_DASH_URL, (haveAuth ? " (Using auth)" : ""), (haveToken ? " (Using token)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : ""), pListenCfg->max); } if((pListenCfg->urlCapabilities & URL_CAP_STATUS)) { LOG(X_INFO("status available at "URL_HTTP_FMT_STR"%s%s%s"), URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_STATUS_URL, (haveAuth ? " (Using auth)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : "")); } if((pListenCfg->urlCapabilities & URL_CAP_PIP)) { LOG(X_INFO("pip available at "URL_HTTP_FMT_STR"%s%s%s"), URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_PIP_URL, (haveAuth ? " (Using auth)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : "")); } if((pListenCfg->urlCapabilities & URL_CAP_CONFIG)) { LOG(X_INFO("config available at "URL_HTTP_FMT_STR"%s%s%s"), URL_HTTP_FMT_ARGS2(pListenCfg, FORMAT_NETADDR(sa, tmp, sizeof(tmp))), VSX_CONFIG_URL, (haveAuth ? " (Using auth)" : ""), (pListenCfg->pCfg->cfgShared.livepwd ? " (Using password)" : "")); } //fprintf(stderr, "LISTENER THREAD %s %s:%d active:%d cap: 0x%x\n", pListenCfg->netflags & NETIO_FLAG_SSL_TLS ? "SSL" : "", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port), pListenCfg->active, pListenCfg->urlCapabilities); // // Service any client connections on the live listening port // rc = srvlisten_loop(pListenCfg, srv_cmd_proc); pthread_mutex_lock(&pListenCfg->mtx); pListenCfg->pnetsockSrv = NULL; netio_closesocket(&netsocksrv); pthread_mutex_unlock(&pListenCfg->mtx); LOG(X_WARNING("HTTP listener thread exiting with code: %d"), rc); logutil_tid_remove(pthread_self()); return; }