{"payload":{"allShortcutsEnabled":false,"fileTree":{"":{"items":[{"name":"src","path":"src","contentType":"directory"},{"name":"win32","path":"win32","contentType":"directory"},{"name":".gitignore","path":".gitignore","contentType":"file"},{"name":"LICENSE.txt","path":"LICENSE.txt","contentType":"file"},{"name":"Makefile","path":"Makefile","contentType":"file"},{"name":"README.txt","path":"README.txt","contentType":"file"},{"name":"Skeleton-3.1-Index-Specification.txt","path":"Skeleton-3.1-Index-Specification.txt","contentType":"file"},{"name":"Skeleton-3.2-Index-Specification.txt","path":"Skeleton-3.2-Index-Specification.txt","contentType":"file"},{"name":"Skeleton-3.3-Index-Specification.txt","path":"Skeleton-3.3-Index-Specification.txt","contentType":"file"},{"name":"Skeleton-4.0-Index-Specification.txt","path":"Skeleton-4.0-Index-Specification.txt","contentType":"file"},{"name":"build-indexer.sh","path":"build-indexer.sh","contentType":"file"},{"name":"build-validator.sh","path":"build-validator.sh","contentType":"file"}],"totalCount":12}},"fileTreeProcessingTime":4.141927,"foldersToFetch":[],"repo":{"id":302698,"defaultBranch":"master","name":"OggIndex","ownerLogin":"cpearce","currentUserCanPush":false,"isFork":false,"isEmpty":false,"createdAt":"2009-09-10T02:55:19.000Z","ownerAvatar":"https://avatars.githubusercontent.com/u/97721?v=4","public":true,"private":false,"isOrgOwned":false},"symbolsExpanded":false,"treeExpanded":true,"refInfo":{"name":"master","listCacheKey":"v0:1613517748.629211","canEdit":false,"refType":"branch","currentOid":"773242c08d5c10dcf2a823e7555386488e553d3f"},"path":"Skeleton-4.0-Index-Specification.txt","currentUser":null,"blob":{"rawLines":["Ogg Skeleton 4.0 with Keyframe Index\r","Chris Pearce, Mozilla Corporation\r","9 June 2010\r","\r","\r","OVERVIEW\r","\r","Seeking in an Ogg file is typically implemented as a bisection search \r","over the pages in the file. The Ogg physical bitstream is bisected and \r","the next Ogg page's end-time is extracted. The bisection continues until \r","it reaches an Ogg page with an end-time close enough to the seek target \r","time. However in media containing streams which have keyframes and \r","interframes, such as Theora streams, your bisection search won't \r","necessarily terminate at a keyframe. Thus if you begin decoding after your\r","first bisection terminates, you're likely to only get partial incomplete\r","frames, with \"visual artifacts\", until you decode up to the next keyframe.\r","So to eliminate these visual artifacts, after the first bisection\r","terminates, you must extract the keyframe's timestamp from the last Theora\r","page's granulepos, and seek again back to the start of the keyframe and\r","decode forward until you reach the frame at the seek target. \r","\r","This is further complicated by the fact that packets often span multiple \r","Ogg pages, and that Ogg pages from different streams can be interleaved \r","between spanning packets. \r","\r","The bisection method above works fine for seeking in local files, but \r","for seeking in files served over the Internet via HTTP, each bisection \r","or non sequential read can trigger a new HTTP request, which can have \r","very high latency, making seeking very slow. \r","\r","\r","SEEKING WITH AN INDEX\r","\r","The Skeleton 4.0 bitstream attempts to alleviate this problem, by \r","providing an index of periodic keyframes for every content stream in an \r","Ogg segment. Note that the Skeleton 4.0 track only holds data for the \r","segment or \"link\" in which it resides. So if two Ogg files are concatenated\r","together (\"chained\"), the Skeleton 4.0's keyframe indexes in the first Ogg\r","segment (the first \"link\" in the \"chain\") do not contain information\r","about the keyframes in the second Ogg segment (the second link in the chain).\r","\r","Each content track has a separate index, which is stored in its own \r","packet in the Skeleton 4.0 track. The index for streams without the \r","concept of a keyframe, such as Vorbis streams, can instead record the \r","time position at periodic intervals, which achieves the same result. \r","When this document refers to keyframes, it also implicitly refers to these\r","independent periodic samples from keyframe-less streams. \r","\r","All the Skeleton 4.0 track's pages appear in the header pages of the Ogg \r","segment. This means the all the keyframe indexes are immediately \r","available once the header packets have been read when playing the media\r","over a network connection. \r","\r","For every content stream in an Ogg segment, the Ogg index bitstream \r","provides seek algorithms with an ordered table of \"key points\". A key \r","point is intrinsically associated with exactly one stream, and stores the\r","offset, o, of the last page which lies before all data required to decode\r","the keyframe, as well as the presentation time of the keyframe t, as a\r","fraction of seconds.\r","\r","The offset is relative from the beginning of the Ogg segment, and is exactly\r","the first byte of the a page in the indexed stream, so if you seek to a\r","keypoint's offset and don't find the beginning of a page there, or you find\r","a page from another stream, you can assume that the Ogg segment has been\r","modified since the index was constructed, and the index can be considered\r","invalid. The time t is the keyframe's presentation time corresponding to the\r","granulepos, and is represented as a fraction in seconds. Note that if a\r","stream requires any preroll, this will be accounted for in the time stored\r","in the keypoint. \r","\r","The Skeleton 4.0 track contains one index for each content stream in the \r","file. To seek in an Ogg file which contains keyframe indexes, first\r","construct the set which contains every active streams' last keypoint which\r","has time less than or equal to the seek target time. This tells you a known\r","point on every stream which lies before the seek target. Then from that set\r","of key points, select the key point with the smallest byte offset. You then\r","verify that there's a page from the keypoint's stream found at exactly that\r","offset, and if so, you can begin decoding. You are guaranteed to pass\r","keyframes on all streams with time less than or equal to your seek target\r","time while decoding up to the seek target. However if you don't encounter\r","a keyframe with the same presentation time as is stored in the keypoint,\r","then the index is invalid (possibly the file has been changed without\r","updating the index) and you must either fallback to a bisection search, or\r","keep decoding if you've landed \"close enough\" to the seek target.\r","\r","Be aware that you cannot assume that any or all Ogg files will contain \r","keyframe indexes, so when implementing Ogg seeking, you must gracefully\r","fall-back to a bisection search or other seek algorithm when the index\r","is not present, or when it is invalid.\r","\r","The Skeleton 4.0 index packets also stores meta data about the segment in \r","which it resides. It stores the timestamps of the first and last samples\r","in its track. This also allows you to determine the duration of the\r","indexed Ogg media without having to decode the start and end of the\r","Ogg segment to calculate the difference (which is the duration). With the\r","index packets storing the start and end times of every track, you can\r","calculate the duration as the end time of the last active stream minus the\r","start time of first active stream.\r","\r","The Skeleton 4.0 BOS packet contains the length of the indexed segment\r","in bytes. This is so that if the seek target is outside of the indexed range,\r","you can immediately move to the next/previous segment and either seek using\r","that segment's index, or narrow the bisection window if that segment has no\r","index. You can also use the segement length to verify if the index is valid.\r","If the contents of the segment have changed, it's highly likely that the\r","length of the segment has changed as well. When you load the segment's\r","header pages, you should check the length of the physical segment, and if it\r","doesn't match the length stored in the Skeleton header packet, you know that\r","either the index is out of date, or the file has been chained since indexing.\r","\r","The Skeleton 4.0 BOS packet also contains the offset of the first non header\r","page in the Ogg segment. This means that if you wish to delay loading of an\r","index for whatever reason, you can skip forward to that offset, and start\r","decoding from that offset forwards.\r","\r","When using the index to seek, you must verify that the index is still \r","correct. You can consider the index invalid if any of the following are true:\r","\r"," 1. The segment doesn't end at the segment length offset stored in the\r"," Skeleton BOS packet (note that a new \"link\" in a \"chain\" can start\r"," at the end of the segment), or\r"," 2. after a seek to a keypoint's offset, you don't land exactly on a page\r"," boundary, or\r"," 3. after a seek to a keypoint's offset, you don't land on a page which\r"," belongs to that keypoint's stream.\r","\r","While loading the Skeleton BOS header, you should always check the Skeleton\r","version field to ensure your decoder correctly knows how to parse the Skeleton\r","track. \r","\r","Be aware that a keyframe index may not index all keyframes in the Ogg segment,\r","it may only index periodic keyframes instead.\r","\r","\r","FORMAT SPECIFICATION \r","\r","Unless otherwise specified, all integers and fields in the bitstream are \r","encoded with the least significant bit coming first in each byte. \r","Integers and fields comprising of more than one byte are encoded least \r","significant byte first (i.e. little endian byte order). \r","\r","The Skeleton 4.0 track is intended to be backwards compatible with the \r","Skeleton 3.0 specification, available at \r","http://www.xiph.org/ogg/doc/skeleton.html . Unless specified \r","differently here, it is safe to assume that anything specified for a \r","Skeleton 3.0 track holds for a Skeleton 4.0 track. \r","\r","As per the Skeleton 3.0 track, an Ogg segment containing a Skeleton 4.0 track\r","must begin with a \"fishead\" BOS packet on a page by itself, with the \r","following format: \r","\r","1. Identifier: 8 bytes, \"fishead\\0\".\r","2. Version major: 2 Byte unsigned integer denoting the major version (4)\r","3. Version minor: 2 Byte unsigned integer denoting the minor version (0)\r","4. Presentationtime numerator: 8 Byte signed integer\r","5. Presentationtime denominator: 8 Byte signed integer\r","6. Basetime numerator: 8 Byte signed integer\r","7. Basetime denominator: 8 Byte signed integer\r","8. UTC [ISO8601]: a 20 Byte string containing a UTC time\r","13. [NEW] The length of the segment, in bytes: 8 byte unsigned integer,\r"," 0 if unknown.\r","14. [NEW] The offset of the first non-header page in bytes: 8 byte unsigned\r"," integer, 0 if unknown.\r","\r","In Skeleton 4.0 the \"fisbone\" packets contain two new compulsory\r","message-header fields \"Role\" and \"Name\". The fisbone packets still follow\r","after the other streams' BOS pages and secondary header pages. \r","\r","Before the Skeleton EOS page in the segment header pages come the \r","Skeleton 4.0 keyframe index packets. There should be one index packet for\r","each content track in the Ogg segment, but index packets are not required\r","for a Skeleton 4.0 track to be considered valid. Each keypoint in the index\r","is stored in a \"keypoint\", which in turn stores an offset, and timestamp.\r","In order to save space, the offsets and timestamps are stored as\r","deltas, and then variable byte-encoded. The offset and timestamp deltas\r","store the difference between the keypoint's offset and timestamp from the\r","previous keypoint's offset and timestamp. So to calculate the page offset\r","of a keypoint you must sum the offset deltas of up to and including the\r","keypoint in the index.\r","\r","The variable byte encoded integers are encoded using 7 bits per byte to\r","store the integer's bits, and the high bit is set in the last byte used\r","to encode the integer. The bits and bytes are in little endian byte order.\r","For example, the integer 7843, or 0001 1110 1010 0011 in binary, would be\r","stored as two bytes: 0xBD 0x23, or 1011 1101 0010 0011 in binary.\r","\r","Each index packet contains the following: \r","\r","1. Identifier 6 bytes: \"index\\0\". Bytes [0...5].\r","2. The serialno of the stream this index applies to, as a 4 byte field.\r"," Bytes [6...9].\r","3. The number of keypoints in this index packet, 'n' as a 8 byte\r"," unsigned integer. This can be 0. Bytes [10...17].\r","4. The timestamp denominator for this stream, as an 8 byte signed\r"," integer. All timestamps, including keypoint timestamps, first and\r"," last sample timestamps are fractions of seconds over this denominator.\r"," This must not be 0. Bytes [18...25].\r","5. First-sample-time numerator: 8 byte signed integer representing\r"," the numerator for the presentation time of the first sample in the track.\r"," Divide this by the timestamp denominator to determine the presentation\r"," time of the first sample in seconds. Bytes [26..33].\r","6. Last-sample-time numerator: 8 byte signed integer representing the end\r"," time of the last sample in the track. Divide this by the timestamp\r"," denominator to determine the end time of the last sample in seconds.\r"," Bytes [34..41].\r","7. 'n' key points, each of which contain, in the following order:\r"," - the keyframe's page's byte offset delta, as a variable byte encoded\r"," integer. This is the number of bytes that this keypoint is after the\r"," preceeding keypoint's offset, or from the start of the segment if this\r"," is the first keypoint. The keypoint's page start is therefore the sum\r"," of the byte-offset-deltas of all the keypoints which come before it.\r"," - the presentation time numerator delta, of a key frame which starts on\r"," or shortly after the page at the keypoint's offset, as a variable byte\r"," encoded integer. This is the difference from the previous keypoint's\r"," timestamp numerator. The keypoint's timestamp numerator is therefore\r"," the sum of all the timestamp numerator deltas up to and including the\r"," keypoint's. Divide the timestamp numerator sum by the timestamp\r"," denominator stored earlier in the index packet to determine the\r"," presentation time of the keyframe in seconds.\r","\r","The key points are stored in increasing order by offset (and thus by \r","presentation time as well).\r","\r","The byte offsets stored in keypoints are relative to the start of the Ogg\r","bitstream segment. So if you have a physical Ogg bitstream made up of two\r","chained Oggs, the offsets in the second Ogg segment's bitstream's index\r","are relative to the beginning of the second Ogg in the chain, not the first.\r","Also note that if a physical Ogg bitstream is made up of chained Oggs, the\r","presence of an index in one segment does not imply that there will be an\r","index in any other segment. \r","\r","The exact number of keyframes used to construct key points in the index \r","is up to the indexer, but to limit the index size, we recommend \r","including at most one key point per every 64KB of data, or every 2000ms, \r","whichever is least frequent. \r","\r","As per the Skeleton 3.0 track, the last packet in the Skeleton 4.0 track \r","is an empty EOS packet. \r"],"stylingDirectives":[[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]],"colorizedLines":null,"csv":null,"csvError":null,"dependabotInfo":{"showConfigurationBanner":false,"configFilePath":null,"networkDependabotPath":"/cpearce/OggIndex/network/updates","dismissConfigurationNoticePath":"/settings/dismiss-notice/dependabot_configuration_notice","configurationNoticeDismissed":null},"displayName":"Skeleton-4.0-Index-Specification.txt","displayUrl":"https://github.com/cpearce/OggIndex/blob/master/Skeleton-4.0-Index-Specification.txt?raw=true","headerInfo":{"blobSize":"12.9 KB","deleteTooltip":"You must be signed in to make or propose changes","editTooltip":"You must be signed in to make or propose changes","ghDesktopPath":"https://desktop.github.com","isGitLfs":false,"onBranch":true,"shortPath":"20ca041","siteNavLoginPath":"/login?return_to=https%3A%2F%2Fgithub.com%2Fcpearce%2FOggIndex%2Fblob%2Fmaster%2FSkeleton-4.0-Index-Specification.txt","isCSV":false,"isRichtext":false,"toc":null,"lineInfo":{"truncatedLoc":"238","truncatedSloc":"202"},"mode":"file"},"image":false,"isCodeownersFile":null,"isPlain":false,"isValidLegacyIssueTemplate":false,"issueTemplate":null,"discussionTemplate":null,"language":"Text","languageID":372,"large":false,"planSupportInfo":{"repoIsFork":null,"repoOwnedByCurrentUser":null,"requestFullPath":"/cpearce/OggIndex/blob/master/Skeleton-4.0-Index-Specification.txt","showFreeOrgGatedFeatureMessage":null,"showPlanSupportBanner":null,"upgradeDataAttributes":null,"upgradePath":null},"publishBannersInfo":{"dismissActionNoticePath":"/settings/dismiss-notice/publish_action_from_dockerfile","releasePath":"/cpearce/OggIndex/releases/new?marketplace=true","showPublishActionBanner":false},"rawBlobUrl":"https://github.com/cpearce/OggIndex/raw/master/Skeleton-4.0-Index-Specification.txt","renderImageOrRaw":false,"richText":null,"renderedFileInfo":null,"shortPath":null,"symbolsEnabled":true,"tabSize":8,"topBannersInfo":{"overridingGlobalFundingFile":false,"globalPreferredFundingPath":null,"showInvalidCitationWarning":false,"citationHelpUrl":"https://docs.github.com/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-citation-files","actionsOnboardingTip":null},"truncated":false,"viewable":true,"workflowRedirectUrl":null,"symbols":{"timed_out":false,"not_analyzed":true,"symbols":[]}},"copilotInfo":null,"copilotAccessAllowed":false,"csrf_tokens":{"/cpearce/OggIndex/branches":{"post":"FZbQow17-9120N0PV51IelHe0V5vZCE4xRx-uBil4GoGiuRdrsZHk6X8-J9CM-IRXFCIUjoAfnMFalrMvkhYYQ"},"/repos/preferences":{"post":"etA7LN22ttoUoStZM1rKcYBGitr2OxbHTx4PxMdOOtWzGygKn-BNPp0zLdZHBgq77MWbQjaQ2oaCRnGzSVUH1A"}}},"title":"OggIndex/Skeleton-4.0-Index-Specification.txt at master ยท cpearce/OggIndex"}