//--------------------------------------------------------------------- std::vector<char> OgreMeshDeserializer::readGeometryVertexBuffer(unsigned int vertexCount) { std::vector<char> vertices; unsigned short bindIndex, vertexSize; // unsigned short bindIndex; // Index to bind this buffer to readShorts(m_stream, &bindIndex, 1); // unsigned short vertexSize; // Per-vertex size, must agree with declaration at this index readShorts(m_stream, &vertexSize, 1); { // Check for vertex data header unsigned short headerID; headerID = readChunk(m_stream); if (headerID != M_GEOMETRY_VERTEX_BUFFER_DATA) { std::runtime_error("Can't find vertex buffer data area"); } // Create / populate vertex buffer vertices.resize(vertexCount * vertexSize); m_stream.read(vertices.data(), vertexCount * vertexSize); } return vertices; }
//--------------------------------------------------------------------- void BackgroundFileSerializer::readLayer( Ogre::DataStreamPtr &stream ,Layer *pDest, size_t layer_index ) { if( !pDest->enabled ) return; uint16 tmp[4], sprite_count; readShorts( stream, tmp, 4 ); pDest->width = tmp[0]; pDest->height = tmp[1]; sprite_count = tmp[2]; pDest->unknown_06 = tmp[3]; switch( layer_index ) { case 1: readShorts( stream, pDest->unknown_08, 3 ); case 2: case 3: readShorts( stream, pDest->unknown_0E, 4 ); } stream->skip( 2 * 2 ); // 2 * uint16 unused; m_layer_index = layer_index; readVector( stream, pDest->sprites, sprite_count ); removeBuggySprites( pDest->sprites ); }
//--------------------------------------------------------------------- OgreMeshDeserializer::VertexElement OgreMeshDeserializer::readGeometryVertexElement() { VertexElement element{}; // unsigned short source; // buffer bind source readShorts(m_stream, &element.source, 1); // unsigned short type; // VertexElementType readShorts(m_stream, &element.vType, 1); // unsigned short semantic; // VertexElementSemantic readShorts(m_stream, &element.vSemantic, 1); // unsigned short offset; // start offset in buffer in bytes readShorts(m_stream, &element.offset, 1); // unsigned short index; // index of the semantic readShorts(m_stream, &element.index, 1); return element; }
void StatefulMeshSerializer::determineFileFormat(DataStreamPtr stream) { determineEndianness(stream); // Read header and determine the version unsigned short headerID; // Read header ID readShorts(stream, &headerID, 1); if (headerID != HEADER_CHUNK_ID) { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "File header not found", "MeshSerializer::importMesh"); } // Read version mMeshFileVersion = readString(stream); // Jump back to start stream->seek(0); #if OGRE_ENDIAN == OGRE_ENDIAN_BIG mMeshFileEndian = mFlipEndian ? ENDIAN_LITTLE : ENDIAN_BIG; #else mMeshFileEndian = mFlipEndian ? ENDIAN_BIG : ENDIAN_LITTLE; #endif }
//--------------------------------------------------------------------- void SkeletonSerializer::readFileHeader(DataStreamPtr& stream) { unsigned short headerID; // Read header ID readShorts(stream, &headerID, 1); if (headerID == HEADER_STREAM_ID_EXT) { // Read version String ver = readString(stream); if ((ver != "[Serializer_v1.10]") && (ver != "[Serializer_v1.80]")) { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Invalid file: version incompatible, file reports " + String(ver), "Serializer::readFileHeader"); } mVersion = ver; } else { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Invalid file: no header", "Serializer::readFileHeader"); } }
//--------------------------------------------------------------------- void SkeletonSerializer::readBone(DataStreamPtr& stream, Skeleton* pSkel) { // char* name String name = readString(stream); // unsigned short handle : handle of the bone, should be contiguous & start at 0 unsigned short handle; readShorts(stream, &handle, 1); // Create new bone Bone* pBone = pSkel->createBone(name, handle); // Vector3 position : position of this bone relative to parent Vector3 pos; readObject(stream, pos); pBone->setPosition(pos); // Quaternion orientation : orientation of this bone relative to parent Quaternion q; readObject(stream, q); pBone->setOrientation(q); #if OGRE_SERIALIZER_VALIDATE_CHUNKSIZE // Hack to fix chunk size validation: mChunkSizeStack.back() += calcStringSize(name); #endif // TODO: don't depend on mCurrentstreamLen in next skeleton format! // Currently we use wrong chunk sizes, but we can't fix it, because we depend on mCurrentstreamLen // Do we have scale? if (mCurrentstreamLen > calcBoneSizeWithoutScale(pSkel, pBone)) { Vector3 scale; readObject(stream, scale); pBone->setScale(scale); } }
//--------------------------------------------------------------------- void Serializer::readFileHeader(DataStreamPtr& stream) { unsigned short headerID; // Read header ID readShorts(stream, &headerID, 1); if (headerID == HEADER_STREAM_ID) { // Read version String ver = readString(stream); if (ver != mVersion) { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Invalid file: version incompatible, file reports " + String(ver) + " Serializer is version " + mVersion, "Serializer::readFileHeader"); } } else { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Invalid file: no header", "Serializer::readFileHeader"); } }
//--------------------------------------------------------------------- void SkeletonSerializer::readBone(DataStreamPtr& stream, Skeleton* pSkel) { // char* name String name = readString(stream); // unsigned short handle : handle of the bone, should be contiguous & start at 0 unsigned short handle; readShorts(stream, &handle, 1); // Create new bone Bone* pBone = pSkel->createBone(name, handle); // Vector3 position : position of this bone relative to parent Vector3 pos; readObject(stream, pos); pBone->setPosition(pos); // Quaternion orientation : orientation of this bone relative to parent Quaternion q; readObject(stream, q); pBone->setOrientation(q); // Do we have scale? if (mCurrentstreamLen > calcBoneSizeWithoutScale(pSkel, pBone)) { Vector3 scale; readObject(stream, scale); pBone->setScale(scale); } }
unsigned short Serializer::readChunk() { unsigned short id; readShorts(&id, 1); readInts(&mCurrentStreamLen, 1); return id; }
//--------------------------------------------------------------------- unsigned short Serializer::readChunk(DataStreamPtr& stream) { unsigned short id; readShorts(stream, &id, 1); readInts(stream, &mCurrentstreamLen, 1); return id; }
//--------------------------------------------------------------------- void BackgroundFileSerializer::readFileHeader( Ogre::DataStreamPtr &stream ) { uint16 data[2]; readShorts( stream, data, 2 ); m_header.unused = data[0]; m_header.sort_sprites_by_palette = data[1]; }
//---------------------------------------------------------------------- void WalkmeshFileSerializer::readObject( Ogre::DataStreamPtr &stream ,WalkmeshFileSerializer::Access &pDest ) { uint16 tmp[ ACCESS_COMPONENT_COUNT ]; readShorts( stream, tmp, ACCESS_COMPONENT_COUNT ); pDest.a = tmp[0]; pDest.b = tmp[1]; pDest.c = tmp[2]; }
//---------------------------------------------------------------------- void WalkmeshFileSerializer::readObject( Ogre::DataStreamPtr &stream ,Ogre::Vector3 &pDest ) { sint16 tmp[ VERTEX_COMPONENT_COUNT ]; readShorts( stream, reinterpret_cast<uint16*>( tmp ), VERTEX_COMPONENT_COUNT ); pDest.x = static_cast<Ogre::Real>( tmp[0] ); pDest.y = static_cast<Ogre::Real>( tmp[1] ); pDest.z = static_cast<Ogre::Real>( tmp[2] ); }
//--------------------------------------------------------------------- unsigned short OgreMeshDeserializer::readChunk(std::istream& stream) { unsigned short id; readShorts(stream, &id, 1); readInts(stream, &mCurrentstreamLen, 1); return id; }
//--------------------------------------------------------------------- void SkeletonSerializer::readBoneParent(DataStreamPtr& stream, Skeleton* pSkel) { // All bones have been created by this point Bone *child, *parent; unsigned short childHandle, parentHandle; // unsigned short handle : child bone readShorts(stream, &childHandle, 1); // unsigned short parentHandle : parent bone readShorts(stream, &parentHandle, 1); // Find bones parent = pSkel->getBone(parentHandle); child = pSkel->getBone(childHandle); // attach parent->addChild(child); }
//--------------------------------------------------------------------- void MeshSerializer::importMesh(DataStreamPtr& stream, Mesh* pDest) { determineEndianness(stream); // Read header and determine the version unsigned short headerID; // Read header ID readShorts(stream, &headerID, 1); if (headerID != HEADER_CHUNK_ID) { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "File header not found", "MeshSerializer::importMesh"); } // Read version String ver = readString(stream); // Jump back to start stream->seek(0); // Find the implementation to use MeshSerializerImpl* impl = 0; for (MeshVersionDataList::iterator i = mVersionData.begin(); i != mVersionData.end(); ++i) { if ((*i)->versionString == ver) { impl = (*i)->impl; break; } } if (!impl) OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Cannot find serializer implementation for " "mesh version " + ver, "MeshSerializer::importMesh"); // Call implementation impl->importMesh(stream, pDest, mListener); // Warn on old version of mesh if (ver != mVersionData[0]->versionString) { LogManager::getSingleton().logMessage("WARNING: " + pDest->getName() + " is an older format (" + ver + "); you should upgrade it as soon as possible" + " using the OgreMeshTool tool.", LML_CRITICAL); } if(mListener) mListener->processMeshCompleted(pDest); }
//--------------------------------------------------------------------- void SkeletonSerializer::importSkeleton(DataStreamPtr& stream, Skeleton* pSkel) { // Determine endianness (must be the first thing we do!) determineEndianness(stream); // Check header readFileHeader(stream); pushInnerChunk(stream); unsigned short streamID = readChunk(stream); while(!stream->eof()) { switch (streamID) { case SKELETON_BLENDMODE: { // Optional blend mode uint16 blendMode; readShorts(stream, &blendMode, 1); pSkel->setBlendMode(static_cast<SkeletonAnimationBlendMode>(blendMode)); break; } case SKELETON_BONE: readBone(stream, pSkel); break; case SKELETON_BONE_PARENT: readBoneParent(stream, pSkel); break; case SKELETON_ANIMATION: readAnimation(stream, pSkel); break; case SKELETON_ANIMATION_LINK: readSkeletonAnimationLink(stream, pSkel); break; default: break; } streamID = readChunk(stream); } // Assume bones are stored in the binding pose pSkel->setBindingPose(); popInnerChunk(stream); }
//--------------------------------------------------------------------- void ParseOgre::importMesh(DataStreamPtr& stream, OgreMesh* pDest) { determineEndianness(stream); // Read header and determine the version unsigned short headerID;// = *(U16*)p; // Read header ID //stream.g readShorts(stream, &headerID, 1); //Read(&headerID,sizeof(headerID),1,pData,uiPos); if (headerID != HEADER_CHUNK_ID) { // OGRE_EXCEPT(0, "File header not found", // "ParseOgre::importMesh"); } // Read version AString ver = readString(stream); // AChar version[MAX_NAME]; // ReadStr(pData,version,uiPos); // ver = version; // Jump back to start stream->seek(0); // Find the implementation to use ParseOgreImplMap::iterator impl = mImplementations.find(ver); if (impl == mImplementations.end()) { // OGRE_EXCEPT(0, "Cannot find serializer implementation for " // "current version " + ver, "ParseOgre::importMesh"); } // Call implementation impl->second->importMesh(stream, pDest, mListener); // Warn on old version of mesh if (ver != msCurrentVersion) { // LogManager("WARNING: " + pDest->getName() + // " is an older format (" + ver + "); you should upgrade it as soon as possible" + // " using the OgreMeshUpgrade tool."); } }
//--------------------------------------------------------------------- void OgreMeshDeserializer::readFileHeader(std::istream& stream) { unsigned short headerID; // Read header ID readShorts(stream, &headerID, 1); if (headerID == HEADER_STREAM_ID) { // Read version std::string ver = readString(stream); if (ver != OGRE_MESH_VERSION) { std::runtime_error("Invalid file: version incompatible, file reports " + ver + " Deserializer is version " + OGRE_MESH_VERSION); } } else { std::runtime_error("Invalid file: no header"); } }
//--------------------------------------------------------------------- unsigned short Serializer::readChunk(DataStreamPtr& stream) { #if OGRE_SERIALIZER_VALIDATE_CHUNKSIZE size_t pos = stream->tell(); #endif unsigned short id; readShorts(stream, &id, 1); readInts(stream, &mCurrentstreamLen, 1); #if OGRE_SERIALIZER_VALIDATE_CHUNKSIZE if (!mChunkSizeStack.empty() && !stream->eof()){ if (pos != static_cast<size_t>(mChunkSizeStack.back()) && mReportChunkErrors){ LogManager::getSingleton().logMessage("Corrupted chunk detected! Stream name: '" + stream->getName() + "' Chunk id: " + StringConverter::toString(id)); } mChunkSizeStack.back() = pos + mCurrentstreamLen; } #endif return id; }
//--------------------------------------------------------------------- void MeshSerializer::importMesh(DataStreamPtr& stream, Mesh* pDest) { determineEndianness(stream); // Read header and determine the version unsigned short headerID; // Read header ID readShorts(stream, &headerID, 1); if (headerID != HEADER_CHUNK_ID) { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "File header not found", "MeshSerializer::importMesh"); } // Read version String ver = readString(stream); // Jump back to start stream->seek(0); // Find the implementation to use MeshSerializerImplMap::iterator impl = mImplementations.find(ver); if (impl == mImplementations.end()) { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Cannot find serializer implementation for " "current version " + ver, "MeshSerializer::importMesh"); } // Call implementation impl->second->importMesh(stream, pDest, mListener); // Warn on old version of mesh if (ver != msCurrentVersion) { LogManager::getSingleton().logMessage("WARNING: " + pDest->getName() + " is an older format (" + ver + "); you should upgrade it as soon as possible" + " using the OgreMeshUpgrade tool."); } }
//--------------------------------------------------------------------- void SkeletonSerializer::readAnimationTrack(DataStreamPtr& stream, Animation* anim, Skeleton* pSkel) { // unsigned short boneIndex : Index of bone to apply to unsigned short boneHandle; readShorts(stream, &boneHandle, 1); // Find bone Bone *targetBone = pSkel->getBone(boneHandle); // Create track NodeAnimationTrack* pTrack = anim->createNodeTrack(boneHandle, targetBone); // Keep looking for nested keyframes if (!stream->eof()) { pushInnerChunk(stream); unsigned short streamID = readChunk(stream); while(streamID == SKELETON_ANIMATION_TRACK_KEYFRAME && !stream->eof()) { readKeyFrame(stream, pTrack, pSkel); if (!stream->eof()) { // Get next stream streamID = readChunk(stream); } } if (!stream->eof()) { // Backpedal back to start of this stream if we've found a non-keyframe backpedalChunkHeader(stream); } popInnerChunk(stream); } }
static spAttachment *readAttachment(spSkeletonBinary *self, spSkin *skin, int slotIndex, const char *attachmentName) { spAttachment *attachment = NULL; float scale = self->scale; char *name = readString(self); if (name == NULL) name = (char *)attachmentName; switch ((spAttachmentType)readByte(self)) { case SP_ATTACHMENT_REGION: { spRegionAttachment *region; char *path = readString(self); if (path == NULL) path = name; attachment = spAttachmentLoader_createAttachment(self->attachmentLoader, skin, SP_ATTACHMENT_REGION, name, path); region = SUB_CAST(spRegionAttachment, attachment); if (path) { region->path = copyString(path); } region->rotation = readFloat(self); region->x = readFloat(self) * scale; region->y = readFloat(self) * scale; region->scaleX = readFloat(self); region->scaleY = readFloat(self); region->width = readFloat(self) * scale; region->height = readFloat(self) * scale; readColor(self, ®ion->r, ®ion->g, ®ion->b, ®ion->a); spRegionAttachment_updateOffset(region); spAttachmentLoader_configureAttachment(self->attachmentLoader, attachment); break; } case SP_ATTACHMENT_BOUNDING_BOX: { spBoundingBoxAttachment *boundingBox; int vertexCount = readVarint(self, true); attachment = spAttachmentLoader_createAttachment(self->attachmentLoader, skin, SP_ATTACHMENT_BOUNDING_BOX, name, name); boundingBox = SUB_CAST(spBoundingBoxAttachment, attachment); readVertices(self, SUPER(boundingBox), vertexCount); SUPER(boundingBox)->worldVerticesLength = vertexCount << 1; spAttachmentLoader_configureAttachment(self->attachmentLoader, attachment); break; } case SP_ATTACHMENT_MESH: { spMeshAttachment *mesh; int vertexCount; char *path = readString(self); if (path == NULL) path = name; attachment = spAttachmentLoader_createAttachment(self->attachmentLoader, skin, SP_ATTACHMENT_MESH, name, path); mesh = SUB_CAST(spMeshAttachment, attachment); if (path) { mesh->path = copyString(path); } readColor(self, &mesh->r, &mesh->g, &mesh->b, &mesh->a); vertexCount = readVarint(self, true); mesh->regionUVs = readFloats(self, 1, vertexCount << 1); mesh->trianglesCount = readVarint(self, true); mesh->triangles = readShorts(self, mesh->trianglesCount); readVertices(self, SUPER(mesh), vertexCount); SUPER(mesh)->worldVerticesLength = vertexCount << 1; mesh->hullLength = readVarint(self, true) << 1; spMeshAttachment_updateUVs(mesh); spAttachmentLoader_configureAttachment(self->attachmentLoader, attachment); break; } case SP_ATTACHMENT_LINKED_MESH: { spMeshAttachment *mesh; char *parent; char *skinName; char *path = readString(self); if (path == NULL) path = name; attachment = spAttachmentLoader_createAttachment(self->attachmentLoader, skin, SP_ATTACHMENT_LINKED_MESH, name, path); mesh = SUB_CAST(spMeshAttachment, attachment); if (path) { mesh->path = copyString(path); } readColor(self, &mesh->r, &mesh->g, &mesh->b, &mesh->a); skinName = readString(self); parent = readString(self); mesh->inheritDeform = readBoolean(self); addLinkedMesh(self, mesh, skinName, slotIndex, parent); break; } case SP_ATTACHMENT_PATH: { spPathAttachment *path; int vertexCount; attachment = spAttachmentLoader_createAttachment(self->attachmentLoader, skin, SP_ATTACHMENT_PATH, name, NULL); path = SUB_CAST(spPathAttachment, attachment); path->closed = readBoolean(self); path->constantSpeed = readBoolean(self); vertexCount = readVarint(self, true); readVertices(self, SUPER(path), vertexCount); SUPER(path)->worldVerticesLength = vertexCount << 1; path->lengthsLength = vertexCount / 3; path->lengths = MALLOC(float, path->lengthsLength); for (int i = 0; i < path->lengthsLength; i++) { path->lengths[i] = readFloat(self) * self->scale; } break; } } return attachment; }
//--------------------------------------------------------------------- void BackgroundFileSerializer::readObject( Ogre::DataStreamPtr &stream, SpriteData &pDest ) { readObject( stream, pDest.dst ); readShorts( stream, pDest.unknown_04, 2 ); readObject( stream, pDest.src ); readObject( stream, pDest.src2 ); readShort( stream, pDest.width ); readShort( stream, pDest.height ); uint16 size; // width and height are sometimes incorrect in the file if ( m_layer_index < 2 ) { size = 16; } else if ( m_layer_index < BackgroundFile::LAYER_COUNT) { size = 32; } else { OGRE_EXCEPT(Ogre::Exception::ERR_INVALIDPARAMS ,"m_layer_index not set correctly" ,"BackgroundFileSerializer::readObject" ); } pDest.width = pDest.height = size; readShort( stream, pDest.palette_page ); readShort( stream, pDest.depth ); // Force depth values switch ( m_layer_index ) { case 0: pDest.depth = 4095; break; case 2: pDest.depth = 4096; break; case 3: pDest.depth = 0; break; } uint8 animation[2]; stream->read( animation, sizeof( animation ) ); pDest.animation_id = animation[0]; pDest.animation_frame = animation[1]; uint8 has_blending[2]; stream->read( has_blending, sizeof( has_blending ) ); pDest.has_blending[0] = has_blending[0] > 0; pDest.has_blending[1] = has_blending[1] > 0; readShort( stream, pDest.blending ); readShort( stream, pDest.data_page ); readShort( stream, pDest.data_page2 ); // when data_page2 != 0, it must be used instead of data_page (not for the first layer) if ( m_layer_index > 0 && pDest.data_page2 ) { pDest.src = pDest.src2; pDest.data_page = pDest.data_page2; } readShort( stream, pDest.colour_depth ); readObject( stream, pDest.src_big ); pDest.src_big /= src_big_SCALE; stream->skip( 2 * 2 ); // 2 * uint16 unused }
//--------------------------------------------------------------------- void OgreMeshDeserializer::readSubMesh() { unsigned short streamID; std::string materialName = readString(m_stream); bool useSharedVertices; readBools(m_stream, &useSharedVertices, 1); size_t offset = 0; if (!useSharedVertices) { offset = m_vertices.size() / 3; } unsigned int indexCount = 0; readInts(m_stream, &indexCount, 1); bool idx32bit; readBools(m_stream, &idx32bit, 1); if (indexCount > 0) { if (idx32bit) { std::vector<std::uint32_t> indices; indices.resize(indexCount); readInts(m_stream, indices.data(), indexCount); for (auto& index : indices) { m_indices.emplace_back(index + offset); } } else // 16-bit { std::vector<std::uint16_t> indices; indices.resize(indexCount); readShorts(m_stream, indices.data(), indexCount); for (auto& index : indices) { m_indices.emplace_back(index + offset); } } } { // M_GEOMETRY m_stream (Optional: present only if useSharedVertices = false) if (!useSharedVertices) { streamID = readChunk(m_stream); if (streamID != M_GEOMETRY) { std::runtime_error("Missing geometry data in mesh file"); } readGeometry(); } // Find all bone assignments, submesh operation, and texture aliases (if present) if (!m_stream.eof()) { streamID = readChunk(m_stream); while (!m_stream.eof() && (streamID == M_SUBMESH_BONE_ASSIGNMENT || streamID == M_SUBMESH_OPERATION || streamID == M_SUBMESH_TEXTURE_ALIAS)) { skipChunk(m_stream); if (!m_stream.eof()) { streamID = readChunk(m_stream); } } if (!m_stream.eof()) { // Backpedal back to start of m_stream backpedalChunkHeader(m_stream); } } } }
void SkeletonSerializerEx::readAnimationTrack( Ogre::DataStreamPtr& stream, Ogre::Animation* anim, Ogre::Skeleton* pSkel) { // unsigned short boneIndex : Index of bone to apply to unsigned short boneHandle; readShorts(stream, &boneHandle, 1); // Find bone Ogre::Bone *targetBone = pSkel->getBone(boneHandle); // Create track Ogre::NodeAnimationTrack* pTrack = anim->createNodeTrack(boneHandle, targetBone); // Keep looking for nested keyframes if (!stream->eof()) { unsigned short streamID = readChunk(stream); while((streamID == Ogre::SKELETON_ANIMATION_TRACK_KEYFRAME || streamID == 0x4120 ) && !stream->eof()) { if (streamID == 0x4120) { unsigned short len; unsigned short flags; readShorts(stream, &len, 1); readShorts(stream, &flags, 1); float time; for (int i = 0; i < len; i += 1) { readFloats(stream, &time, 1); Ogre::TransformKeyFrame *kf = pTrack->createNodeKeyFrame(time); Ogre::Quaternion rot = Ogre::Quaternion::IDENTITY; if (flags & 1) { readObject(stream, rot); } kf->setRotation(rot); Ogre::Vector3 trans = Ogre::Vector3::ZERO; if (flags & 2) { readObject(stream, trans); } kf->setTranslate(trans); // 为正确解析天龙八部模型的骨骼动画 Ogre::Vector3 scale = Ogre::Vector3::UNIT_SCALE; if (flags & 4) { readObject(stream, scale); } kf->setScale(scale); } } else readKeyFrame(stream, pTrack, pSkel); if (!stream->eof()) { // Get next stream streamID = readChunk(stream); } } if (!stream->eof()) { // Backpedal back to start of this stream if we've found a non-keyframe stream->skip(-STREAM_OVERHEAD_SIZE); } } }