📄 mpeg2ps.c
字号:
sptr->m_stream_id, sptr->m_substream_id); return; } } else { mpeg2ps_message(LOG_ERR, "unknown stream id %x", sptr->m_stream_id); return; } mpeg2ps_message(LOG_INFO, "audio stream %x - freq %u chans %u bitrate %u spf %u", sptr->m_stream_id, sptr->freq, sptr->channels, sptr->bitrate, sptr->samples_per_frame);}/* * clear_stream_buffer - called when we seek to clear out any data in * the buffers */static void clear_stream_buffer (mpeg2ps_stream_t *sptr){ sptr->pes_buffer_on = sptr->pes_buffer_size = 0; sptr->frame_len = 0; sptr->have_frame_loaded = false; sptr->next_pes_ts.have_dts = sptr->next_pes_ts.have_pts = false; sptr->frame_ts.have_dts = sptr->frame_ts.have_pts = false; sptr->lpcm_read_offset = true;}/* * convert_to_msec - convert ts (at 90000) to msec, based on base_ts and * frames_since_last_ts. */static uint64_t convert_ts (mpeg2ps_stream_t *sptr, mpeg2ps_ts_type_t ts_type, uint64_t ts, uint64_t base_ts, uint32_t frames_since_ts){ uint64_t ret, calc; ret = ts - base_ts; if (sptr->is_video) { // video ret += frames_since_ts * sptr->ticks_per_frame; } else { // audio if (frames_since_ts != 0 && sptr->freq != 0) { calc = (frames_since_ts * 90000 * sptr->samples_per_frame) / sptr->freq; ret += calc; } } if (ts_type == TS_MSEC) ret /= TO_U64(90); // * 1000 / 90000#if 0 printf("stream %x - ts "U64" base "U64" frames since %d ret "U64"\n", sptr->m_stream_id, ts, base_ts, frames_since_ts, ret);#endif return ret;}/* * find_stream_from_id - given the stream, get the sptr. * only used in inital set up, really. APIs use index into * video_streams and audio_streams arrays. */static mpeg2ps_stream_t *find_stream_from_id (mpeg2ps_t *ps, uint8_t stream_id, uint8_t substream){ uint8_t ix; if (stream_id >= 0xe0) { for (ix = 0; ix < ps->video_cnt; ix++) { if (ps->video_streams[ix]->m_stream_id == stream_id) { return ps->video_streams[ix]; } } } else { for (ix = 0; ix < ps->audio_cnt; ix++) { if (ps->audio_streams[ix]->m_stream_id == stream_id && (stream_id != 0xbd || substream == ps->audio_streams[ix]->m_substream_id)) { return ps->audio_streams[ix]; } } } return NULL;}/* * add_stream - add a new stream */static bool add_stream (mpeg2ps_t *ps, uint8_t stream_id, uint8_t substream, off_t first_loc, mpeg2ps_ts_t *ts){ mpeg2ps_stream_t *sptr; sptr = find_stream_from_id(ps, stream_id, substream); if (sptr != NULL) return FALSE; // need to add sptr = mpeg2ps_stream_create(stream_id, substream); sptr->first_pes_loc = first_loc; if (ts == NULL || (ts->have_dts == false && ts->have_pts == false)) { sptr->first_pes_has_dts = false; mpeg2ps_message(LOG_CRIT, "stream %x doesn't have start pts", sptr->m_stream_id); } else { sptr->start_dts = ts->have_dts ? ts->dts : ts->pts; sptr->first_pes_has_dts = true; } if (sptr->is_video) { // can't be more than 16 - e0 to ef... ps->video_streams[ps->video_cnt] = sptr; mpeg2ps_message(LOG_DEBUG, "added video stream %x "X64" "U64, stream_id, first_loc, sptr->start_dts); ps->video_cnt++; } else { if (ps->audio_cnt >= 32) { mpeg2ps_stream_destroy(sptr); return FALSE; } mpeg2ps_message(LOG_DEBUG, "added audio stream %x %x "X64" "U64, stream_id, substream, first_loc, sptr->start_dts); ps->audio_streams[ps->audio_cnt] = sptr; ps->audio_cnt++; } return TRUE;}static void check_fd_for_stream (mpeg2ps_t *ps, mpeg2ps_stream_t *sptr){ if (sptr->m_fd != FDNULL) return; sptr->m_fd = file_open(ps->filename);}/* * advance_frame - when we're reading frames, this indicates that we're * done. We will call this when we read a frame, but not when we * seek. It allows us to leave the last frame we're seeking in the * buffer */static void advance_frame (mpeg2ps_stream_t *sptr){ sptr->pes_buffer_on += sptr->frame_len; sptr->have_frame_loaded = false; if (sptr->frame_ts.have_dts || sptr->frame_ts.have_pts) { if (sptr->frame_ts.have_dts) sptr->last_ts = sptr->frame_ts.dts; else sptr->last_ts = sptr->frame_ts.pts; sptr->frames_since_last_ts = 0; } else { sptr->frames_since_last_ts++; }}/* * get_info_for_all_streams - loop through found streams - read an * figure out the info */static void get_info_for_all_streams (mpeg2ps_t *ps){ uint8_t stream_ix, max_ix, av; mpeg2ps_stream_t *sptr; uint8_t *buffer; uint32_t buflen; // av will be 0 for video streams, 1 for audio streams // av is just so I don't have to dup a lot of code that does the // same thing. for (av = 0; av < 2; av++) { if (av == 0) max_ix = ps->video_cnt; else max_ix = ps->audio_cnt; for (stream_ix = 0; stream_ix < max_ix; stream_ix++) { if (av == 0) sptr = ps->video_streams[stream_ix]; else sptr = ps->audio_streams[stream_ix]; if (file_seek_to(ps->fd, sptr->first_pes_loc) != sptr->first_pes_loc) { mpeg2ps_message(LOG_ERR, "stream %x error - can't seek to "X64" %s", sptr->m_stream_id, sptr->first_pes_loc, strerror(errno)); } // we don't open a seperate file descriptor yet (only when they // start reading or seeking). Use the one from the ps. sptr->m_fd = ps->fd; // for now clear_stream_buffer(sptr); if (mpeg2ps_stream_read_frame(sptr, &buffer, &buflen, false) == FALSE) { mpeg2ps_message(LOG_CRIT, "Couldn't read frame of stream %x", sptr->m_stream_id); sptr->m_stream_id = 0; sptr->m_fd = FDNULL; continue; } get_info_from_frame(sptr, buffer, buflen); //printf("got stream av %d %d\n", av, sptr->first_pes_has_dts); // here - if (sptr->first_pes_has_dts == false) should be processed if (sptr->first_pes_has_dts == false) { uint32_t frames_from_beg = 0; bool have_frame; do { advance_frame(sptr); have_frame = mpeg2ps_stream_read_frame(sptr, &buffer, &buflen, false); frames_from_beg++; } while (have_frame && sptr->frame_ts.have_dts == false && sptr->frame_ts.have_pts == false && frames_from_beg < 1000); if (have_frame == false || (sptr->frame_ts.have_dts == false && sptr->frame_ts.have_pts == false)) { mpeg2ps_message(LOG_ERR, "can't find initial pts of stream %x - have_frame %d cnt %u", sptr->m_stream_id, have_frame, frames_from_beg); } else { sptr->start_dts = sptr->frame_ts.have_dts ? sptr->frame_ts.dts : sptr->frame_ts.pts; if (sptr->is_video) { sptr->start_dts -= frames_from_beg * sptr->ticks_per_frame; } else { uint64_t conv; conv = sptr->samples_per_frame * 90000; conv /= (uint64_t)sptr->freq; sptr->start_dts -= conv; } mpeg2ps_message(LOG_DEBUG, "stream %x - calc start pts of "U64, sptr->m_stream_id, sptr->start_dts); } } clear_stream_buffer(sptr); sptr->m_fd = FDNULL; } }}/* * mpeg2ps_scan_file - read file, grabbing all the information that * we can out of it (what streams exist, timing, etc). */static void mpeg2ps_scan_file (mpeg2ps_t *ps){ uint8_t stream_id, stream_ix, substream, av_ix, max_cnt; uint16_t pes_len, pes_left; mpeg2ps_ts_t ts; off_t loc, first_video_loc = 0, first_audio_loc = 0; off_t check, orig_check; mpeg2ps_stream_t *sptr; bool valid_stream; uint8_t *buffer; uint32_t buflen; bool have_ts; ps->end_loc = file_size(ps->fd); orig_check = check = MAX(ps->end_loc / 50, 200 * 1024); /* * This part reads and finds the streams. We check up until we * find audio and video plus a little, with a max of either 200K or * the file size / 50 */ loc = 0; while (read_to_next_pes_header(ps->fd, &stream_id, &pes_len) && loc < check) { pes_left = pes_len; if (stream_id >= 0xbd && stream_id < 0xf0) { loc = file_location(ps->fd) - 6; if (read_pes_header_data(ps->fd, pes_len, &pes_left, &have_ts, &ts) == FALSE) { return; } valid_stream = FALSE; substream = 0; if (stream_id == 0xbd) { if (file_read_bytes(ps->fd, &substream, 1) == FALSE) { return; } pes_left--; // remove byte we just read if ((substream >= 0x80 && substream < 0x90) || (substream >= 0xa0 && substream < 0xb0)){ valid_stream = TRUE; } } else if (stream_id >= 0xc0 && stream_id <= 0xef) { // audio and video valid_stream = TRUE; }#if 0 mpeg2ps_message(LOG_DEBUG, "stream %x %x loc "X64" pts %d dts %d\n", stream_id, substream, loc, ts.have_pts, ts.have_dts);#endif if (valid_stream) { if (add_stream(ps, stream_id, substream, loc, &ts)) { // added if (stream_id >= 0xe0) { if (ps->video_cnt == 1) { first_video_loc = loc; } } else if (ps->audio_cnt == 1) { first_audio_loc = loc; } if (ps->audio_cnt > 0 && ps->video_cnt > 0) { off_t diff; if (first_audio_loc > first_video_loc) diff = first_audio_loc - first_video_loc; else diff = first_video_loc - first_audio_loc; diff *= 2; diff += first_video_loc; if (diff < check) { check = diff; } } } } } file_skip_bytes(ps->fd, pes_left); } if (ps->video_cnt == 0 && ps->audio_cnt == 0) { return; } /* * Now, we go to close to the end, and try to find the last * dts that we can */ // printf("to end "X64"\n", end - orig_check); file_seek_to(ps->fd, ps->end_loc - orig_check); while (read_to_next_pes_header(ps->fd, &stream_id, &pes_len)) { loc = file_location(ps->fd) - 6; if (stream_id == 0xbd || (stream_id >= 0xc0 && stream_id < 0xf0)) { if (read_pes_header_data(ps->fd, pes_len, &pes_left, &have_ts, &ts) == FALSE) { return; } if (stream_id == 0xbd) { if (file_read_bytes(ps->fd, &substream, 1) == FALSE) { return; } pes_left--; // remove byte we just read if (!((substream >= 0x80 && substream < 0x90) || (substream >= 0xa0 && substream < 0xb0))) { file_skip_bytes(ps->fd, pes_left); continue; } } else { substream = 0; } sptr = find_stream_from_id(ps, stream_id, substream); if (sptr == NULL) { mpeg2ps_message(LOG_INFO, "adding stream from end search %x %x", stream_id, substream); add_stream(ps, stream_id, substream, 0, NULL); sptr = find_stream_from_id(ps, stream_id, substream); } if (sptr != NULL && have_ts) { sptr->end_dts = ts.have_dts ? ts.dts : ts.pts; sptr->end_dts_loc = loc; }#if 0 printf("loc "X64" stream %x %x", loc, stream_id, substream); if (ts.have_pts) printf(" pts "U64, ts.pts); if (ts.have_dts) printf(" dts "U64, ts.dts); printf("\n");#endif file_skip_bytes(ps->fd, pes_left); } } /* * Now, get the info for all streams, so we can use it again * we could do this before the above, I suppose */ get_info_for_all_streams(ps); ps->first_dts = MAX_UINT64; /* * we need to find the earliest start pts - we use that to calc * the rest of the timing, so we're 0 based. */ for (av_ix = 0; av_ix < 2; av_ix++) { if (av_ix == 0) max_cnt = ps->video_cnt; else max_cnt = ps->audio_cnt; for (stream_ix = 0; stream_ix < max_cnt; stream_ix++) { sptr = av_ix == 0 ? ps->video_streams[stream_ix] : ps->audio_streams[stream_ix]; if (sptr != NULL && sptr->start_dts < ps->first_dts) { ps->first_dts = sptr->start_dts; } } } mpeg2ps_message(LOG_INFO, "start ps is "U64, ps->first_dts); /* * Now, for each thread, we'll start at the last pts location, and * read the number of frames. This will give us a max time */ for (av_ix = 0; av_ix < 2; av_ix++) { if (av_ix == 0) max_cnt = ps->video_cnt; else max_cnt = ps->audio_cnt; for (stream_ix = 0; stream_ix < max_cnt; stream_ix++) { uint32_t frame_cnt_since_last; sptr = av_ix == 0 ? ps->video_streams[stream_ix] : ps->audio_streams[stream_ix]; // pick up here - find the final time... if (sptr->end_dts_loc != 0) { //printf("end loc "U64"\n", sptr->end_dts_loc); file_seek_to(ps->fd, sptr->end_dts_loc); sptr->m_fd = ps->fd; frame_cnt_since_last = 0; clear_stream_buffer(sptr); while (mpeg2ps_stream_read_frame(sptr, &buffer, &buflen, true)) { //printf("loc "U64"\n", file_location(sptr->m_fd)); frame_cnt_since_last++; } sptr->m_fd = FDNULL; clear_stream_buffer(sptr); mpeg2ps_message(LOG_DEBUG, "stream %x last ts "U64" since last %u\n", sptr->m_stream_id, sptr->end_dts, frame_cnt_since_last); ps->max_time = MAX(ps->max_time, convert_ts(sptr, TS_MSEC, sptr->end_dts, ps->first_dts, frame_cnt_since_last)); } else { mpeg2ps_message(LOG_DEBUG, "stream %x no end dts", sptr->m_stream_id); } } } ps->max_dts = (ps->max_time * 90) + ps->first_dts; mpeg2ps_message(LOG_DEBUG, "max time is "U64, ps->max_time); file_seek_to(ps->fd, 0);}/************************************************************************* * API routines *************************************************************************/uint64_t mpeg2ps_get_max_time_msec (mpeg2ps_t *ps) { return ps->max_time;}uint32_t mpeg2ps_get_video_stream_count (mpeg2ps_t *ps){ return ps->video_cnt;}// routine to check stream number passed.static bool invalid_video_streamno (mpeg2ps_t *ps, uint streamno){ if (streamno >= NUM_ELEMENTS_IN_ARRAY(ps->video_streams)) return true; if (ps->video_streams[streamno] == NULL) return true; return false;}char *mpeg2ps_get_video_stream_name (mpeg2ps_t *ps, uint streamno){ if (invalid_video_streamno(ps, streamno)) { return NULL; } if (ps->video_streams[streamno]->have_h264) { return h264_get_profile_level_string(ps->video_streams[streamno]->video_profile, ps->video_streams[streamno]->video_level); } if (ps->video_streams[streamno]->have_mpeg2) { return strdup(mpeg2_type(ps->video_streams[streamno]->video_profile)); } return strdup("Mpeg-1");}mpeg2ps_video_type_t mpeg2ps_get_video_stream_type (mpeg2ps_t *ps, uint streamno){ if (invalid_video_streamno(ps, streamno)) { return MPEG_AUDIO_UNKNOWN; } if (ps->video_streams[streamno]->have_h264) return MPEG_VIDEO_H264; return ps->video_streams[streamno]->have_mpeg2 ? MPEG_VIDEO_MPEG2 : MPEG_VIDEO_MPEG1;}uint32_t mpeg2ps_get_video_stream_width (mpeg2ps_t *ps, uint streamno){ if (invalid_video_streamno(ps, streamno)) { return 0; } return ps->video_streams[streamno]->w;}uint32_t mpeg2ps_get_video_stream_height (mpeg2ps_t *ps, uint streamno){ if (invalid_video_streamno(ps, streamno)) { return 0; } return ps->video_streams[streamno]->h;}double mpeg2ps_get_video_stream_bitrate (mpeg2ps_t *ps, uint streamno){ if (invalid_video_streamno(ps, streamno)) { return 0; } return ps->video_streams[streamno]->bit_rate;}double mpeg2ps_get_video_stream_framerate (mpeg2ps_t *ps, uint streamno){ if (invalid_video_streamno(ps, streamno)) { return 0; } return ps->video_streams[streamno]->frame_rate;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -