📄 mpeg2_transport.c
字号:
/* * mpeg2t_malloc_es_work - create or clean a mpeg2t_frame_t for an * elementary stream. */void mpeg2t_malloc_es_work (mpeg2t_es_t *es_pid, uint32_t frame_len){ uint8_t *frameptr;#if 0 mpeg2t_message(LOG_DEBUG, "es %x malloc %d", es_pid->pid.pid, es_pid->have_ps_ts);#endif if (es_pid->work == NULL || es_pid->work->frame_len < frame_len) { if (es_pid->work != NULL) { free(es_pid->work); es_pid->work = NULL; } frameptr = (uint8_t *)malloc(sizeof(mpeg2t_frame_t) + frame_len); if (frameptr == NULL) return; es_pid->work = (mpeg2t_frame_t *)frameptr; es_pid->work->frame = frameptr + sizeof(mpeg2t_frame_t); es_pid->work->frame_len = frame_len; } es_pid->work->next_frame = NULL; es_pid->work->have_ps_ts = es_pid->have_ps_ts; es_pid->work->have_dts = es_pid->have_dts; es_pid->work->ps_ts = es_pid->ps_ts; es_pid->work->dts = es_pid->dts; es_pid->have_ps_ts = 0; es_pid->have_dts = 0;}/* * mpeg2t_finished_es_work - when we have a frame, this is * called to save the frame on the list (if so configured) */void mpeg2t_finished_es_work (mpeg2t_es_t *es_pid, uint32_t frame_len){ mpeg2t_frame_t *p;#if 1 mpeg2t_message(LOG_WARNING, "pid %x pts %d "U64" listing %d", es_pid->pid.pid, es_pid->work->have_ps_ts, es_pid->work->ps_ts, es_pid->save_frames);#endif SDL_LockMutex(es_pid->list_mutex); if (es_pid->save_frames == 0) { mpeg2t_malloc_es_work(es_pid, es_pid->work->frame_len); } else { es_pid->work->frame_len = frame_len; if (es_pid->list == NULL) { es_pid->list = es_pid->work; } else { p = es_pid->list; while (p->next_frame != NULL) p = p->next_frame; p->next_frame = es_pid->work; } es_pid->frames_in_list++; es_pid->work = NULL; } es_pid->work_loaded = 0; SDL_UnlockMutex(es_pid->list_mutex);}/* * mpeg2t_get_es_list_head - get the first frame on the list */mpeg2t_frame_t *mpeg2t_get_es_list_head (mpeg2t_es_t *es_pid){ mpeg2t_frame_t *p; SDL_LockMutex(es_pid->list_mutex); p = es_pid->list; if (p != NULL) { es_pid->list = p->next_frame; p->next_frame = NULL; es_pid->frames_in_list--; } SDL_UnlockMutex(es_pid->list_mutex); return p;}void mpeg2t_free_frame (mpeg2t_frame_t *fptr){ free(fptr);}/* * mpeg2t_process_es - process a transport stream pak for an * elementary stream */static int mpeg2t_process_es (mpeg2t_t *ptr, mpeg2t_pid_t *ifptr, const uint8_t *buffer){ uint32_t buflen; uint32_t pes_len; const uint8_t *esptr; mpeg2t_es_t *es_pid = (mpeg2t_es_t *)ifptr; int read_pes_options; uint8_t stream_id; uint32_t nextcc, pakcc; int ret; int ac; int have_psts; uint64_t pts; uint32_t offset;#ifdef DEBUG_MPEG2T uint32_t ix;#endif ac = mpeg2t_adaptation_control(buffer); // Note to self - if ac is 0x3, we may have to check // the discontinuity indicator in the adapation control - this // can cause a cc to be non-sequential, as well. // ASDF - not implemented yet nextcc = ifptr->lastcc; if (!(ac == 0 || ac == 2)) { // don't add to continuity counter if adaptation counter is 0 or 2 nextcc = (nextcc + 1) & 0xf; } pakcc = mpeg2t_continuity_counter(buffer); if (nextcc != pakcc) { // Note - this message will occur for the first packet mpeg2t_message(LOG_ERR, "cc error in PID %x should be %d is %d %x", ifptr->pid, nextcc, pakcc, buffer[3]); clean_es_data(es_pid); } ifptr->lastcc = pakcc; if (es_pid->save_frames == 0 && es_pid->report_psts == 0 && es_pid->info_loaded > 0) { // mpeg2t_message(LOG_INFO, "PID %x not processing", ifptr->pid); return 0; } buflen = 188; // process pas pointer esptr = mpeg2t_transport_payload_start(buffer, &buflen); if (esptr == NULL) return -1; have_psts = 0; if (mpeg2t_payload_unit_start_indicator(buffer) != 0) { // start of PES packet es_pid->peshdr_loaded = 1; if ((esptr[0] != 0) || (esptr[1] != 0) || (esptr[2] != 1)) { mpeg2t_message(LOG_ERR, "Illegal start to PES packet - pid %x - %02x %02x %02x", ifptr->pid, esptr[0], esptr[1], esptr[2]); return -1; } stream_id = es_pid->stream_id = esptr[3]; pes_len = (esptr[4] << 8) | esptr[5]; esptr += 6; buflen -= 6; read_pes_options = 0; // do we have header extensions switch ((stream_id & 0x70) >> 4) { default: if ((stream_id == 0xbd) || (stream_id >= 0xf3 && stream_id <= 0xf7) || (stream_id >= 0xf9)) { read_pes_options = 1; break; } // fall through case 4: case 5: case 6: if (esptr[2] <= buflen - 3) { // multiple PES for header read_pes_options = 1; } else { // don't have enough to read the header } break; } es_pid->have_ps_ts = 0; es_pid->have_dts = 0; if (read_pes_options) { if (esptr[2] + 3U > buflen) { return 0; } mpeg2t_message(LOG_INFO, "pid %x pes bits %x %x", es_pid->pid.pid, esptr[0], esptr[1]);#ifdef DEBUG_MPEG2T if ((esptr[1] & 0x20)) { mpeg2t_message(LOG_INFO, "pid %x has ESCR", es_pid->pid.pid); } if ((esptr[1] & 0x10)) { mpeg2t_message(LOG_INFO, "pid %x has ES rate", es_pid->pid.pid); }#endif offset = 3; //mpeg2t_read_pes_options(es_pid, esptr); if (esptr[1] & 0x80) { // read presentation timestamp#if 0 mpeg2t_message(LOG_INFO, "Stream %x %02x %02x %02x", stream_id, esptr[0], esptr[1], esptr[2]); mpeg2t_message(LOG_INFO, "PTS %02x %02x %02x %02x %02x", esptr[3], esptr[4], esptr[5], esptr[6], esptr[7]);#endif if (((esptr[1] >> 6) & 0x3) != ((esptr[3] >> 4) & 0xf)) { mpeg2t_message(LOG_ERR, "PID %x Timestamp flag value not same %x %x", es_pid->pid.pid, esptr[1], esptr[2]); return -1; } pts = ((esptr[3] >> 1) & 0x7); pts <<= 8; pts |= esptr[4]; pts <<= 7; pts |= ((esptr[5] >> 1) & 0x7f); pts <<= 8; pts |= esptr[6]; pts <<= 7; pts |= ((esptr[7] >> 1) & 0x7f); es_pid->have_ps_ts = 1; es_pid->ps_ts = pts; // (pts * M_64) / (90 * M_64); // give msec have_psts = 1; mpeg2t_message(LOG_INFO, "pid %x psts "U64, es_pid->pid.pid, es_pid->ps_ts); offset = 8; } // this is how we would read the dts, if we wanted to use it if (esptr[1] & 0x40) { pts = ((esptr[offset] >> 1) & 0x7); pts <<= 8; pts |= esptr[offset + 1]; pts <<= 7; pts |= ((esptr[offset + 2] >> 1) & 0x7f); pts <<= 8; pts |= esptr[offset + 3]; pts <<= 7; pts |= ((esptr[offset + 4] >> 1) & 0x7f); mpeg2t_message(LOG_INFO, "pid %x dts "U64, es_pid->pid.pid, pts); have_psts = 1; es_pid->have_dts = 1; es_pid->dts = pts; }#ifdef DEBUG_MPEG2T mpeg2t_message(LOG_INFO, "pid %x pes buflen %d\n", es_pid->pid.pid, esptr[2]); for (ix = 0; ix < esptr[2]; ix += 4) { mpeg2t_message(LOG_ERR, "pid %x %d - %02x %02x %02x %02x", es_pid->pid.pid, ix, esptr[3 + ix], esptr[4 + ix], esptr[5 + ix], esptr[6 + ix]); }#endif buflen -= esptr[2] + 3; esptr += esptr[2] + 3; if (pes_len > 0) pes_len -= esptr[2] + 3; } // process esptr, buflen if (buflen == 0) { es_pid->have_ps_ts = 0; es_pid->have_dts = 0; return 0; } mpeg2t_message(LOG_DEBUG, "%x PES start stream id %02x len %u (%u)", ifptr->pid, stream_id, pes_len, buflen); } else { // 0 in Payload start - process frame at start read_pes_options = 0; } // have start of data is at esptr, buflen data ret = 0; switch (es_pid->stream_type) { case 1: case 2: // mpeg1 or mpeg2 video ret = process_mpeg2t_mpeg_video(es_pid, esptr, buflen); break; case 3: case 4: // mpeg1/mpeg2 audio (mp3 codec) ret = process_mpeg2t_mpeg_audio(es_pid, esptr, buflen); break; case 0x10: ret = process_mpeg2t_mpeg4_video(es_pid, esptr, buflen); break; case 129: ret = process_mpeg2t_ac3_audio(es_pid, esptr, buflen); break; case 0x1b: ret = process_mpeg2t_h264_video(es_pid, esptr, buflen); break; case 0xf: // aac break; } //es_pid->have_ps_ts = 0; if (have_psts != 0 && es_pid->report_psts != 0) ret = 1; return ret;}static void add_unknown_pid (mpeg2t_t *ptr, uint16_t rpid){ mpeg2t_unk_pid_t *p = MALLOC_STRUCTURE(mpeg2t_unk_pid_t); p->next_unk = ptr->unk_pids; ptr->unk_pids = p; p->pid = rpid; p->count = 1;} static mpeg2t_unk_pid_t *look_up_unknown_pid (mpeg2t_t *ptr, uint16_t rpid){ mpeg2t_unk_pid_t *p = ptr->unk_pids; while (p != NULL) { if (p->pid == rpid) return p; p = p->next_unk; } return NULL;}/* * mpeg2t_process_buffer - API routine that allows us to * process a buffer filled with transport streams */ mpeg2t_pid_t *mpeg2t_process_buffer (mpeg2t_t *ptr, const uint8_t *buffer, uint32_t buflen, uint32_t *buflen_used){ uint32_t offset; uint32_t remaining; uint32_t used; uint16_t rpid; mpeg2t_pid_t *pidptr; int ret; used = 0; remaining = buflen; mpeg2t_message(LOG_DEBUG, "start processing buffer - len %d", buflen); if (buflen < 188) { *buflen_used = buflen; return NULL; } while (used < buflen) { offset = mpeg2t_find_sync_byte(buffer, remaining); //mpeg2t_message(LOG_DEBUG, "offset %d", offset); if (offset >= remaining) { mpeg2t_message(LOG_ERR, "sync not found in buffer"); *buflen_used = buflen; return NULL; } remaining -= offset; buffer += offset; used += offset; if (remaining < 188) { *buflen_used = used; return NULL; } // we have a complete buffer rpid = mpeg2t_pid(buffer);#if 1 mpeg2t_message(LOG_DEBUG, "Buffer- PID %x start %d cc %d %x", rpid, mpeg2t_payload_unit_start_indicator(buffer), mpeg2t_continuity_counter(buffer), buffer[3]);#endif if (rpid == 0x1fff) { // just skip } else { // look up pid in table pidptr = mpeg2t_lookup_pid(ptr, rpid); if (pidptr != NULL) { // okay - we've got a valid pid ptr switch (pidptr->pak_type) { case MPEG2T_PAS_PAK: ret = mpeg2t_process_pas(ptr, buffer); if (ret > 0) { *buflen_used = used + 188; return pidptr; } break; case MPEG2T_PROG_MAP_PAK: ret = mpeg2t_process_pmap(ptr, pidptr, buffer); if (ret > 0) { *buflen_used = used + 188; return pidptr; } break; case MPEG2T_ES_PAK: if (mpeg2t_process_es(ptr, pidptr, buffer) > 0) { *buflen_used = used + 188; return pidptr; } break; } } else { if (ptr->pas.programs_added >= ptr->pas.programs) { mpeg2t_unk_pid_t *unk; unk = look_up_unknown_pid(ptr, rpid); if (unk != NULL) { unk->count++; if ((unk->count % 1000) == 0) { mpeg2t_message(LOG_ERR, "unknown pid %x received %u packets", rpid, unk->count); } } else { add_unknown_pid(ptr, rpid); mpeg2t_message(LOG_ALERT, "pid %x received - not in pas/program map table", rpid); } } } } used += 188; buffer += 188; remaining -= 188; } *buflen_used = buflen; return NULL;}/* * create_mpeg2_tranport - create the basic structure - we always * need a PAS, so we create one (PAS has a pid of 0). */ mpeg2t_t *create_mpeg2_transport (void){ mpeg2t_t *ptr; ptr = MALLOC_STRUCTURE(mpeg2t_t); memset(ptr, 0, sizeof(mpeg2t_t)); ptr->pas.pid.pak_type = MPEG2T_PAS_PAK; ptr->pas.pid.next_pid = NULL; ptr->pas.pid.pid = 0; ptr->program_count = 0; ptr->program_maps_recvd = 0; ptr->pid_mutex = SDL_CreateMutex(); return (ptr);}static void clean_pid (mpeg2t_pid_t *pidptr){ CHECK_AND_FREE(pidptr->data);}static void clean_es_pid (mpeg2t_es_t *es_pid){ mpeg2t_frame_t *p; clean_es_data(es_pid); do { p = mpeg2t_get_es_list_head(es_pid); if (p != NULL) free(p); } while (p != NULL); CHECK_AND_FREE(es_pid->work); CHECK_AND_FREE(es_pid->es_data); SDL_DestroyMutex(es_pid->list_mutex);} void delete_mpeg2t_transport (mpeg2t_t *ptr){ mpeg2t_pid_t *pidptr, *p; mpeg2t_pmap_t *pmap; mpeg2t_unk_pid_t *unk; pidptr = ptr->pas.pid.next_pid; while (pidptr != NULL) { mpeg2t_message(LOG_CRIT, "cleaning %p pid %x", pidptr, pidptr->pid); switch (pidptr->pak_type) { case MPEG2T_ES_PAK: clean_es_pid((mpeg2t_es_t *)pidptr); break; case MPEG2T_PROG_MAP_PAK: pmap = (mpeg2t_pmap_t *)pidptr; CHECK_AND_FREE(pmap->prog_info); clean_pid(pidptr); break; case MPEG2T_PAS_PAK: clean_pid(pidptr); break; } p = pidptr; pidptr = pidptr->next_pid; free(p); } clean_pid(&ptr->pas.pid); while (ptr->unk_pids != NULL) { unk = ptr->unk_pids; ptr->unk_pids = unk->next_unk; free(unk); } SDL_DestroyMutex(ptr->pid_mutex); free(ptr);}/* * Other API routines */void *mpeg2t_get_userdata (mpeg2t_pid_t *pid){ return pid->userdata;}void mpeg2t_set_userdata (mpeg2t_pid_t *pid, void *ud){ pid->userdata = ud;}void mpeg2t_set_frame_status (mpeg2t_es_t *es_pid, uint32_t flags){ es_pid->save_frames = 0; es_pid->report_psts = 0; if (flags == MPEG2T_PID_NOTHING) { mpeg2t_clear_frames(es_pid); return; } if ((flags & MPEG2T_PID_REPORT_PSTS) != 0) { es_pid->report_psts = 1; } if ((flags & MPEG2T_PID_SAVE_FRAME) != 0) { es_pid->save_frames = 1; }}void mpeg2t_clear_frames (mpeg2t_es_t *es_pid) { SDL_LockMutex(es_pid->list_mutex); es_pid->have_ps_ts = 0; es_pid->have_dts = 0; es_pid->have_seq_header = 0; es_pid->frames_in_list = 0; while (es_pid->list != NULL) { mpeg2t_frame_t *f = es_pid->list; es_pid->list = f->next_frame; mpeg2t_free_frame(f); } clean_es_data(es_pid); SDL_UnlockMutex(es_pid->list_mutex);} int mpeg2t_write_stream_info (mpeg2t_es_t *es_pid, char *buffer, size_t buflen){ int ret = -1; switch (es_pid->stream_type) { case 1: case 2: // mpeg1 or mpeg2 video //ret = process_mpeg2t_mpeg_video(es_pid, esptr, buflen); ret = mpeg2t_mpeg_video_info(es_pid, buffer, buflen); break; case 3: case 4: // mpeg1/mpeg2 audio (mp3 codec) ret = mpeg2t_mpeg_audio_info(es_pid, buffer, buflen); break; case 0x10: ret = mpeg2t_mpeg4_video_info(es_pid, buffer, buflen); break; case 129: ret = mpeg2t_ac3_audio_info(es_pid, buffer, buflen); break; case 0x1b: ret = mpeg2t_h264_video_info(es_pid, buffer, buflen); break; case 0xf: // aac break; } return ret;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -