gmc

package
v0.0.0-...-d4234d6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 14, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package gmc implements the GMC media container format.

Example

Example demonstrates the full lifecycle: create a file, register tracks, write frames, attach session tags, finalize, then reopen and seek.

package main

import (
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/Youngju-Heo/go-container/gmc"
)

func main() {
	path := filepath.Join(os.TempDir(), "gmc_example.gmc")
	defer os.Remove(path)

	// Write side.
	w, err := gmc.Create(path, gmc.CreateOptions{Private: []byte("manifest")})
	if err != nil {
		panic(err)
	}

	video, err := w.AddTrack(gmc.TrackInfo{
		Kind: gmc.KindVideo, Codec: "h264",
		TimebaseNum: 1, TimebaseDen: 90000,
	})
	if err != nil {
		panic(err)
	}

	// Session metadata, written to the fixed tags area at the front of the file.
	w.SetStartTime(time.Unix(0, 0).UTC())
	w.SetTag(gmc.TagLocation, []byte("37.5665,126.9780"))

	// A short GOP: keyframe every 3rd frame, pts stepping by 3000.
	for i := 0; i < 9; i++ {
		err := w.WriteFrame(video, gmc.Frame{
			PTS: uint64(i * 3000), Keyframe: i%3 == 0, Data: []byte{byte(i)},
		})
		if err != nil {
			panic(err)
		}
	}

	// Finalize writes a footer + trailer so the file reopens without a scan.
	if err := w.Finalize(); err != nil {
		panic(err)
	}

	// Read side: reopen the finalized file.
	r, err := gmc.Open(path)
	if err != nil {
		panic(err)
	}
	defer r.Close()

	loc := r.Tags()[gmc.TagLocation]
	fmt.Printf("location: %s\n", loc)

	// Seek to pts 15000 (frame 5): lands on the previous keyframe (frame 3, pts 9000).
	it, err := r.SeekPTS(video, 15000)
	if err != nil {
		panic(err)
	}
	var ptss []uint64
	for it.Next() {
		ptss = append(ptss, it.Frame().PTS)
	}
	if it.Err() != nil {
		panic(it.Err())
	}
	fmt.Printf("seek(15000) starts at pts: %d\n", ptss[0])
	fmt.Printf("frames from there: %d\n", len(ptss))

}
Output:
location: 37.5665,126.9780
seek(15000) starts at pts: 9000
frames from there: 6

Index

Examples

Constants

View Source
const (
	TagStartTime = "gmc.start_time_unix_ns"
	TagLocation  = "gmc.location"
)

Well-known tag keys. The "gmc." prefix is reserved.

View Source
const (

	// Version is the file format version this package reads and writes. Open
	// rejects files carrying any other version, so any opened file is Version.
	Version = formatVersion
)

Variables

View Source
var (
	ErrCorrupt         = errors.New("gmc: corrupt data")
	ErrNonMonotonicPTS = errors.New("gmc: non-monotonic pts within track")
	ErrTagsTooLarge    = errors.New("gmc: tags exceed slot capacity")
	ErrUnknownTrack    = errors.New("gmc: unknown track")
	ErrClosed          = errors.New("gmc: writer closed")
	ErrNoStartTime     = errors.New("gmc: start time tag not set")
)

Functions

This section is empty.

Types

type CreateOptions

type CreateOptions struct {
	Private            []byte        // file-level private data, immutable
	TagsAreaSize       int           // total tags area size (2 slots); default 8 KiB
	CheckpointBytes    int64         // checkpoint trigger by bytes; default 8 MiB
	CheckpointInterval time.Duration // checkpoint trigger by time; default 1s
}

CreateOptions configures a new file.

type Frame

type Frame struct {
	PTS      uint64
	DTS      uint64 // valid only when HasDTS
	HasDTS   bool
	Keyframe bool
	Data     []byte
}

Frame is one media frame / sample / metadata event. Frames are stored in decode order; PTS is the presentation timestamp. DTS (decode timestamp) is optional and stored only when HasDTS is set.

type Iterator

type Iterator struct {
	// contains filtered or unexported fields
}

Iterator walks Data chunks in storage order, filtered by track.

func (*Iterator) Err

func (it *Iterator) Err() error

Err returns the first error encountered, if any.

func (*Iterator) Frame

func (it *Iterator) Frame() Frame

Frame returns the current frame. Valid after Next returned true.

func (*Iterator) Next

func (it *Iterator) Next() bool

Next advances to the next matching frame. It returns false at the end of committed data or on error (check Err).

func (*Iterator) Track

func (it *Iterator) Track() TrackID

Track returns the current frame's track.

type Reader

type Reader struct {
	// contains filtered or unexported fields
}

Reader provides random access and tailing over a GMC file. Live readers (from Writer.NewReader) share the writer's in-memory index and committed size; opened readers own an index built from the footer or a recovery scan.

func Open

func Open(path string) (*Reader, error)

Open opens an existing GMC file. A valid trailer loads everything from the footer in one read; otherwise the file is recovered by a full CRC scan.

func (*Reader) Close

func (r *Reader) Close() error

Close closes the reader's file handle.

func (*Reader) FilePrivate

func (r *Reader) FilePrivate() []byte

FilePrivate returns the immutable file-level private data.

func (*Reader) Finalized

func (r *Reader) Finalized() bool

Finalized reports whether the file was properly closed with a footer and trailer. Live readers always report false (the file is still being written).

func (*Reader) Follow

func (r *Reader) Follow(ctx context.Context, tracks ...TrackID) <-chan TrackFrame

Follow tails the file from the current committed position, delivering frames of the given tracks (all when none specified) as they are committed. The channel closes when the writer finalizes/closes and all remaining data has been delivered, or when ctx is canceled. On a non-live reader it drains existing data and closes.

func (*Reader) LastPTS

func (r *Reader) LastPTS(id TrackID) (uint64, bool)

LastPTS returns the highest committed pts of the track. For live readers it reflects exactly the frames written so far; for opened files it comes from the footer summary or the recovery scan. ok is false when the track is unknown or has no frames.

func (*Reader) LastTime

func (r *Reader) LastTime(id TrackID) (time.Time, bool)

LastTime returns the absolute wall-clock time of LastPTS: StartTime + LastPTS×timebase. ok is false without a start time, an unknown track, or an empty track.

func (*Reader) ReadInterleaved

func (r *Reader) ReadInterleaved(pts uint64, tracks ...TrackID) (*Iterator, error)

ReadInterleaved iterates frames of the given tracks (all tracks when none specified) in storage order, starting at the minimum offset among each track's last sync point at or before pts — so no track misses its own sync point for the target position.

func (*Reader) SeekPTS

func (r *Reader) SeekPTS(id TrackID, pts uint64) (*Iterator, error)

SeekPTS positions an iterator at the last sync point at or before pts on the given track. The iterator yields frames from the sync point onward, so callers receive the decode warm-up frames before the target pts.

func (*Reader) SeekTime

func (r *Reader) SeekTime(t time.Time, tracks ...TrackID) (*Iterator, error)

SeekTime positions an interleaved iterator at the absolute wall-clock time t, converting it into each track's timebase (all tracks when none given). Placement follows ReadInterleaved: the minimum offset among each track's last sync point at or before the converted pts. Requires the gmc.start_time_unix_ns tag; returns ErrNoStartTime otherwise. Times before the start clamp to the beginning of the stream.

func (*Reader) StartTime

func (r *Reader) StartTime() (time.Time, bool)

StartTime decodes the TagStartTime tag if present.

func (*Reader) Summaries

func (r *Reader) Summaries() ([]TrackSummary, int)

Summaries returns per-track footer summaries and the total number of sync points in the index. For finalized files the summaries come from the footer (Frames accurate). For recovered (non-finalized) files there is no footer, so the summary slice is nil; the sync-point count still reflects the recovery scan.

func (*Reader) Tags

func (r *Reader) Tags() map[string][]byte

Tags returns the latest session tags snapshot.

func (*Reader) Tracks

func (r *Reader) Tracks() []TrackInfo

Tracks returns all tracks ordered by ID.

type RepairResult

type RepairResult struct {
	Repaired  bool           // false = already finalized or zero frames (file unchanged)
	Tracks    []TrackInfo    // tracks ordered by ID
	Summaries []TrackSummary // per-track firstPTS/lastPTS/frames (PTS-based, always accurate)
	Frames    int64          // total data frames recovered (sum of Summaries frames)
	Size      int64          // file size in bytes after repair
	StartTime time.Time      // wall-clock of pts 0 from TagStartTime; zero if the tag is absent
	LastTime  time.Time      // wall-clock of the last frame; zero without a start time
}

RepairResult reports the outcome of Repair.

func Repair

func Repair(path string) (RepairResult, error)

Repair turns a GMC file that crashed before finalization into a normal footer-backed file, in place and without rewriting frame data. It is a no-op (Repaired=false) when the file is already finalized or holds zero valid frames. Repair is idempotent and lossless: it only truncates the incomplete bytes past the last valid frame, then appends a footer and trailer.

type TrackFrame

type TrackFrame struct {
	Track TrackID
	Frame Frame
}

TrackFrame is one frame delivered by Follow, tagged with its track.

type TrackID

type TrackID uint16

TrackID identifies a track within a file. Assigned by AddTrack.

type TrackInfo

type TrackInfo struct {
	ID          TrackID
	Kind        TrackKind
	Codec       string
	TimebaseNum uint32
	TimebaseDen uint32
	Private     []byte

	// Reordered selects the write-side validation mode for this track. It is
	// not serialized (like ID, it is meaningless on read). When true the track
	// carries a reordered (B-frame) stream in decode order: only sync-point
	// (keyframe) pts monotonicity is enforced and non-keyframe pts are free.
	// Callers that can supply DTS should prefer the default mode with HasDTS.
	Reordered bool
}

TrackInfo describes one track. ID is ignored on AddTrack input and filled in on read. All tracks share the same time origin: pts 0 is the session origin regardless of per-track timebase.

type TrackKind

type TrackKind uint8

TrackKind is a classification hint; container behavior never depends on it, except that the writer samples index entries for KindAudio tracks.

const (
	KindVideo TrackKind = 0
	KindAudio TrackKind = 1
	KindData  TrackKind = 2
)

type TrackSummary

type TrackSummary struct {
	Track    TrackID
	FirstPTS uint64
	LastPTS  uint64
	Frames   uint64
}

TrackSummary is a per-track storage summary derived from the footer.

type Writer

type Writer struct {
	// contains filtered or unexported fields
}

Writer appends frames to a GMC file. Safe for concurrent use; all writes are serialized by an internal mutex.

func Create

func Create(path string, opts CreateOptions) (*Writer, error)

Create creates a new GMC file. It fails if the file already exists.

func (*Writer) AddTrack

func (w *Writer) AddTrack(info TrackInfo) (TrackID, error)

AddTrack registers a new track. Must be called before writing any frame of that track.

func (*Writer) Close

func (w *Writer) Close() error

Close closes the file without writing a footer. The file remains a valid unfinalized GMC file and reopens through the scan-recovery path.

func (*Writer) Finalize

func (w *Writer) Finalize() error

Finalize writes the consolidated footer and trailer, syncs, and closes the file. The footer is a convenience cache: the file is fully readable through the scan path even without it.

func (*Writer) NewReader

func (w *Writer) NewReader() (*Reader, error)

NewReader returns a reader that shares this writer's in-memory index and committed size, over its own read-only file handle.

func (*Writer) SetStartTime

func (w *Writer) SetStartTime(t time.Time) error

SetStartTime stores the absolute wall-clock time of pts 0 (all tracks share the same time origin) under the TagStartTime key.

func (*Writer) SetTag

func (w *Writer) SetTag(key string, value []byte) error

SetTag adds or updates one session tag. The full snapshot is rewritten into the inactive slot of the tags area (ping-pong), so a torn write can never destroy the previous value.

func (*Writer) Sync

func (w *Writer) Sync() error

Sync flushes file contents to stable storage.

func (*Writer) WriteFrame

func (w *Writer) WriteFrame(id TrackID, fr Frame) error

WriteFrame appends one frame. PTS must be non-decreasing within a track.

Directories

Path Synopsis
Package codec defines the GMC codec conventions that mirror Matroska: Matroska CodecID strings in TrackInfo.Codec, MKV block payload bytes in Frame.Data, and a small private envelope carrying the MKV TrackEntry Audio/Video parameters alongside the original CodecPrivate.
Package codec defines the GMC codec conventions that mirror Matroska: Matroska CodecID strings in TrackInfo.Codec, MKV block payload bytes in Frame.Data, and a small private envelope carrying the MKV TrackEntry Audio/Video parameters alongside the original CodecPrivate.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL