📄 mpeg2t_file.cpp
字号:
/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is MPEG4IP. * * The Initial Developer of the Original Code is Cisco Systems Inc. * Portions created by Cisco Systems Inc. are * Copyright (C) Cisco Systems Inc. 2004. All Rights Reserved. * * Contributor(s): * Bill May wmay@cisco.com *//* * mpeg2t_thread.c */#include "mpeg2t_private.h"#include "player_session.h"#include "player_media.h"#include "player_util.h"#include "our_config_file.h"#include "media_utils.h"#include "codec_plugin_private.h"#include "mpeg2f_bytestream.h"#include "mpeg2t_file.h"#include "mp4av.h"//#define DEBUG_MPEG2F_SEARCH 1#ifdef _WIN32DEFINE_MESSAGE_MACRO(mpeg2f_message, "mpeg2f")#else#define mpeg2f_message(loglevel, fmt...) message(loglevel, "mpeg2f", fmt)#endifint create_media_for_mpeg2t_file (CPlayerSession *psptr, const char *name, int have_audio_driver, control_callback_vft_t *cc_vft){ FILE *ifile; uint8_t buffer[188]; ifile = fopen(name, FOPEN_READ_BINARY); if (ifile == NULL) { psptr->set_message("Couldn't open file %s", name); return -1; } if (fread(buffer, 1, 1, ifile) <= 0) { psptr->set_message("Couldn't read file %s", name); fclose(ifile); return -1; } if (buffer[0] != MPEG2T_SYNC_BYTE) { psptr->set_message("File is not mpeg2 transport %s", name); fclose(ifile); return -1; } CMpeg2tFile *tfile = new CMpeg2tFile(name, ifile); int ret; psptr->session_set_seekable(0); ret = tfile->create(psptr); if (ret < 0) { delete tfile; return ret; } ret = tfile->create_media(psptr, cc_vft); if (ret < 0) { player_error_message("mpeg2t file found"); delete tfile; } psptr->set_session_desc(0, name); return ret;}/* * Close routine - clean up the file when we're done */static void close_mpeg2t_file (void *data){ CMpeg2tFile *f = (CMpeg2tFile *)data; delete f;}CMpeg2tFile::CMpeg2tFile (const char *name, FILE *ifile){ m_ifile = ifile; m_buffer_size = 0; m_buffer_on = 0; m_file_mutex = SDL_CreateMutex(); m_buffer = NULL; m_mpeg2t = NULL;}CMpeg2tFile::~CMpeg2tFile (void){ fclose(m_ifile); CHECK_AND_FREE(m_buffer); if (m_mpeg2t != NULL) delete_mpeg2t_transport(m_mpeg2t); SDL_DestroyMutex(m_file_mutex); m_file_mutex = NULL;}/* * Create - will determine pids and psts ranges in file. Will also * loop through the file and determine CFilePosRec points at percentages */int CMpeg2tFile::create (CPlayerSession *psptr){ m_mpeg2t = create_mpeg2_transport(); if (m_mpeg2t == NULL) { psptr->set_message("Couldn't create mpeg2 transport"); fclose(m_ifile); return -1; } // nice, large buffers to process m_buffer_size_max = 188 * 2000; m_buffer = (uint8_t *)malloc(m_buffer_size_max); if (m_buffer == NULL) { psptr->set_message("Malloc error"); return -1; } m_buffer[0] = MPEG2T_SYNC_BYTE; m_buffer_size = fread(&m_buffer[1], 1, m_buffer_size_max - 1, m_ifile) + 1; bool done = false; mpeg2t_pid_t *pidptr; uint32_t buflen_used; bool have_psts = false; uint64_t earliest_psts = 0; mpeg2t_es_t *es_pid; int olddebuglevel; olddebuglevel = config.get_config_value(CONFIG_MPEG2T_DEBUG); if (olddebuglevel != LOG_DEBUG) mpeg2t_set_loglevel(LOG_CRIT); m_mpeg2t->save_frames_at_start = 1; /* * We need to determine which PIDs are present, and try to establish * a starting psts. We also want to establish what type of video and * audio are in the mix. Note: if we try to run this on a file that * we don't understand the video, this could take a while, because the * info never gets loaded. */ do { m_buffer_on = 0; while (m_buffer_on + 188 < m_buffer_size && done == false) { pidptr = mpeg2t_process_buffer(m_mpeg2t, &m_buffer[m_buffer_on], m_buffer_size - m_buffer_on, &buflen_used); m_buffer_on += buflen_used; if (pidptr != NULL && pidptr->pak_type == MPEG2T_ES_PAK) { es_pid = (mpeg2t_es_t *)pidptr; mpeg2t_frame_t *fptr; // determine earliest PS_TS while ((fptr = mpeg2t_get_es_list_head(es_pid)) != NULL) { if (fptr->have_ps_ts != 0 || fptr->have_dts != 0) { uint64_t ps_ts = 0; bool store_psts = true; if (fptr->have_dts != 0) { ps_ts = fptr->dts; } else { if (es_pid->is_video == 1) { // mpeg2 // video - make sure we get the first I frame, then we can // get the real timestamp if (fptr->frame_type != 1) { store_psts = false; } else { ps_ts = fptr->ps_ts; uint16_t temp_ref = MP4AV_Mpeg3PictHdrTempRef(fptr->frame + fptr->pict_header_offset); ps_ts -= ((temp_ref + 1) * es_pid->tick_per_frame); } } else { ps_ts = fptr->ps_ts; } } if (store_psts) { // when we have the first psts for a ES_PID, turn off // parsing frames for that PID. mpeg2t_set_frame_status(es_pid, MPEG2T_PID_NOTHING); if (have_psts) { earliest_psts = MIN(earliest_psts, ps_ts); } else { earliest_psts = ps_ts; have_psts = true; } } } mpeg2t_free_frame(fptr); } // Each time, search through and see if there are any ES_PIDs // that have not returned a psts. We're done when the info is // loaded for all the es pids. pidptr = m_mpeg2t->pas.pid.next_pid; bool finished = true; while (pidptr != NULL && finished) { if (pidptr->pak_type == MPEG2T_ES_PAK) { es_pid = (mpeg2t_es_t *)pidptr; if (es_pid->info_loaded == 0) { finished = false; } } pidptr = pidptr->next_pid; } done = finished || have_psts; } } if (done == false) { m_buffer_size = fread(m_buffer, 1, m_buffer_size_max, m_ifile); } } while (m_buffer_size >=188 && done == false); if (done == false) { psptr->set_message("Could not find information in TS"); mpeg2t_set_loglevel(olddebuglevel); return -1; }#ifdef DEBUG_MPEG2F_SEARCH mpeg2f_message(LOG_DEBUG, "initial psts is "U64, earliest_psts);#endif m_start_psts = earliest_psts; // Now, we'll try to build a rough index for the file // enable psts reading for the pid for (pidptr = m_mpeg2t->pas.pid.next_pid; pidptr != NULL; pidptr = pidptr->next_pid) { if (pidptr->pak_type == MPEG2T_ES_PAK) { es_pid = (mpeg2t_es_t *)pidptr; mpeg2t_set_frame_status(es_pid, MPEG2T_PID_REPORT_PSTS); } } m_file_record.record_point(0, earliest_psts, 0); fpos_t fpos; uint64_t end; uint64_t perc, cur; // find out the length of the file. struct stat filestat; if (fstat(fileno(m_ifile), &filestat) != 0) { return -1; } end = filestat.st_size; perc = end; // perc is what size of the file to skip through to get a rough // timetable. We want to do 10% chunks, or 100Mb chunks, whichever is // less. while (perc > TO_U64(100000000)) { perc /= 2; } if (perc > (end / TO_U64(10))) { perc = end / TO_U64(10); } if (perc < (end / TO_U64(50))) { perc = end / TO_U64(50); }#ifdef DEBUG_MPEG2F_SEARCH mpeg2f_message(LOG_DEBUG, "perc is "U64" "U64, perc, (perc * TO_U64(100)) / end );#endif cur = perc; bool is_seekable = true; uint64_t last_psts, ts; last_psts = earliest_psts; uint64_t check_end; if (end < m_buffer_size_max * 2) { check_end = end / 2; } else check_end = end - (m_buffer_size_max * 2); // Now - skip to the next perc chunk, and try to find the next psts // we'll record this info. do {#ifdef DEBUG_MPEG2F_SEARCH mpeg2f_message(LOG_DEBUG, "current "U64" end "U64, cur, end);#endif VAR_TO_FPOS(fpos, cur); fsetpos(m_ifile, &fpos); done = false; uint64_t count = 0; m_buffer_on = 0; m_buffer_size = 0; do { if (m_buffer_on + 188 > m_buffer_size) { if (m_buffer_on < m_buffer_size) { memmove(m_buffer, m_buffer + m_buffer_on, m_buffer_size - m_buffer_on); m_buffer_on = m_buffer_size - m_buffer_on; } else { m_buffer_on = 0; } m_buffer_size = fread(m_buffer + m_buffer_on, 1, (188 * 10) - m_buffer_on, m_ifile); count += m_buffer_size - m_buffer_on; m_buffer_size += m_buffer_on; m_buffer_on = 0; if (m_buffer_size < 188) { m_buffer_size = 0; done = true; } } pidptr = mpeg2t_process_buffer(m_mpeg2t, m_buffer + m_buffer_on, m_buffer_size - m_buffer_on, &buflen_used); m_buffer_on += buflen_used; if (pidptr != NULL && pidptr->pak_type == MPEG2T_ES_PAK) { es_pid = (mpeg2t_es_t *)pidptr; // If we have a psts, record it. // If it is less than the previous one, we've got a discontinuity, so // we can't seek. if (es_pid->have_ps_ts || es_pid->have_dts) { ts = es_pid->have_ps_ts ? es_pid->ps_ts : es_pid->dts; if (ts < last_psts) { player_error_message("pid %x psts "U64" is less than prev record point "U64, es_pid->pid.pid, ts, last_psts); cur = end; is_seekable = false; } else {#ifdef DEBUG_MPEG2F_SEARCH mpeg2f_message(LOG_DEBUG, "pid %x psts "U64" %d", pidptr->pid, ts, es_pid->is_video);#endif m_file_record.record_point(cur, ts, 0); } done = true; } } } while (done == false && count < perc / 2); cur += perc; } while (cur < check_end); //mpeg2f_message(LOG_DEBUG, "starting end search"); // Now, we'll go to close to the end of the file, and look for a // final PSTS. This gives us a rough estimate of the elapsed time long seek_offset; seek_offset = 0; seek_offset -= (m_buffer_size_max) * 2; fseek(m_ifile, seek_offset, SEEK_END); m_buffer_on = m_buffer_size = 0; uint64_t max_psts; max_psts = m_start_psts; do { while (m_buffer_on + 188 <= m_buffer_size) { pidptr = mpeg2t_process_buffer(m_mpeg2t,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -