00001 // -*- Mode: c++ -*- 00002 /******************************************************************* 00003 * h264utils 00004 * 00005 * Copyright (C) Her Majesty the Queen in Right of Canada, 2006 00006 * Communications Research Centre (CRC) 00007 * 00008 * Distributed as part of MythTV (www.mythtv.org) 00009 * 00010 * This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; either version 2 of the License, or 00013 * (at your option) any later version. 00014 * 00015 * This program is distributed in the hope that it will be useful, 00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 * GNU General Public License for more details. 00019 * 00020 * You should have received a copy of the GNU General Public License 00021 * along with this program; if not, write to the Free Software 00022 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00023 * 00024 * Contact: 00025 * Francois Lefebvre <francois [dot] lefebvre [at] crc [dot] ca> 00026 * Web: http://www.crc.ca 00027 * 00028 * 2006/04 Jean-Francois Roy for CRC 00029 * Initial release 00030 * 00031 ********************************************************************/ 00032 00033 // C headers 00034 #include <cstring> 00035 00036 // C++ headers 00037 #include <iostream> 00038 00039 // MythTV headers 00040 #include "h264utils.h" 00041 00042 extern "C" { 00043 // from libavcodec 00044 extern const uint8_t *ff_find_start_code(const uint8_t * p, const uint8_t *end, uint32_t * state); 00045 } 00046 00047 namespace H264 00048 { 00049 00050 KeyframeSequencer::KeyframeSequencer() /* throw() */ 00051 { 00052 Reset(); 00053 } 00054 00055 void KeyframeSequencer::Reset(void) /* throw() */ 00056 { 00057 errored = false; 00058 state_changed = false; 00059 00060 sync_accumulator = 0xffffffff; 00061 sync_stream_offset = 0; 00062 00063 first_NAL_byte = H264::NALUnitType::UNKNOWN; 00064 00065 saw_AU_delimiter = false; 00066 saw_first_VCL_NAL_unit = false; 00067 saw_sps = false; 00068 00069 did_evaluate_once = false; 00070 keyframe = false; 00071 keyframe_sync_stream_offset = 0; 00072 } 00073 00074 void KeyframeSequencer::KeyframePredicate( 00075 const uint8_t new_first_NAL_byte) /* throw() */ 00076 { 00077 // new_NAL_type will be current after this method executes 00078 uint8_t new_NAL_type = new_first_NAL_byte & 0x1f; 00079 uint8_t current_NAL_type = first_NAL_byte & 0x1f; 00080 00081 // stage 1: if we're starting a new AU, save the offset 00082 // in case it's an IDR AU 00083 if ((saw_first_VCL_NAL_unit || !did_evaluate_once) && 00084 !saw_AU_delimiter) 00085 { 00086 did_evaluate_once = true; 00087 00088 // stage 1.1: this particular check follows 00089 // ITU-T Rec. H.264 (03/2005) 00090 if (new_NAL_type == NALUnitType::AU_DELIMITER || 00091 current_NAL_type == NALUnitType::END_SEQUENCE) 00092 { 00093 saw_first_VCL_NAL_unit = false; 00094 saw_AU_delimiter = true; 00095 keyframe_sync_stream_offset = sync_stream_offset; 00096 } 00097 00098 /* stage 1.2 is a hack. The correct method would be to 00099 * write down when see those types (and others, such as 00100 * a VCL NAL unit), wait until the next VCL NAL unit, 00101 * and compare it to the last seen VCL NAL unit according 00102 * to ITU-T Rec. H.264 (03/2005) 7.4.1.2.4 00103 */ 00104 00105 // stage 1.2: HACK: this is a fugly guesstimate based on 00106 // Figure 7-1 in 7.4.1.2.3 00107 if (new_NAL_type == NALUnitType::SEI || 00108 new_NAL_type == NALUnitType::SPS || 00109 new_NAL_type == NALUnitType::PPS || 00110 (new_NAL_type > NALUnitType::SPS_EXT && 00111 new_NAL_type < NALUnitType::AUXILIARY_SLICE)) 00112 { 00113 saw_first_VCL_NAL_unit = false; 00114 saw_AU_delimiter = true; 00115 keyframe_sync_stream_offset = sync_stream_offset; 00116 } 00117 } 00118 00119 // stage 2: determine if it's an IDR AU 00120 if (!saw_first_VCL_NAL_unit && !saw_sps && new_NAL_type == NALUnitType::SPS) 00121 { 00122 saw_sps = true; 00123 state_changed = true; 00124 keyframe = false; 00125 } 00126 00127 // stage 3: did we see the AU's first VCL NAL unit yet? 00128 if (!saw_first_VCL_NAL_unit && NALUnitType::IsVCLType(new_NAL_type)) 00129 { 00130 saw_first_VCL_NAL_unit = true; 00131 saw_AU_delimiter = false; 00132 state_changed = true; 00133 if (saw_sps) 00134 keyframe = true; 00135 saw_sps = false; 00136 } 00137 } 00138 00139 uint32_t KeyframeSequencer::AddBytes( 00140 const uint8_t *bytes, 00141 const uint32_t byte_count, 00142 const int64_t stream_offset) /* throw() */ 00143 { 00144 const uint8_t *local_bytes = bytes; 00145 const uint8_t *local_bytes_end = bytes + byte_count; 00146 00147 state_changed = false; 00148 00149 while (local_bytes < local_bytes_end) 00150 { 00151 local_bytes = ff_find_start_code(local_bytes, local_bytes_end, 00152 &sync_accumulator); 00153 00154 if ((sync_accumulator & 0xffffff00) == 0x00000100) 00155 { 00156 uint8_t k = *(local_bytes-1); 00157 sync_stream_offset = stream_offset; 00158 keyframe = false; 00159 00160 KeyframePredicate(k); 00161 first_NAL_byte = k; 00162 00163 return local_bytes - bytes; 00164 } 00165 } 00166 00167 return local_bytes - bytes; 00168 } 00169 00170 bool KeyframeSequencer::IsOnFrame(void) const /* throw() */ 00171 { 00172 // HACK: This could be improved, but it does work if you're 00173 // interested in knowing when you just hit a frame 00174 // (as opposed to still being on one). 00175 return saw_first_VCL_NAL_unit; 00176 } 00177 00178 } // namespace H264
1.5.5