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 ¶
- Constants
- Variables
- type CreateOptions
- type Frame
- type Iterator
- type Reader
- func (r *Reader) Close() error
- func (r *Reader) FilePrivate() []byte
- func (r *Reader) Finalized() bool
- func (r *Reader) Follow(ctx context.Context, tracks ...TrackID) <-chan TrackFrame
- func (r *Reader) LastPTS(id TrackID) (uint64, bool)
- func (r *Reader) LastTime(id TrackID) (time.Time, bool)
- func (r *Reader) ReadInterleaved(pts uint64, tracks ...TrackID) (*Iterator, error)
- func (r *Reader) SeekPTS(id TrackID, pts uint64) (*Iterator, error)
- func (r *Reader) SeekTime(t time.Time, tracks ...TrackID) (*Iterator, error)
- func (r *Reader) StartTime() (time.Time, bool)
- func (r *Reader) Summaries() ([]TrackSummary, int)
- func (r *Reader) Tags() map[string][]byte
- func (r *Reader) Tracks() []TrackInfo
- type RepairResult
- type TrackFrame
- type TrackID
- type TrackInfo
- type TrackKind
- type TrackSummary
- type Writer
- func (w *Writer) AddTrack(info TrackInfo) (TrackID, error)
- func (w *Writer) Close() error
- func (w *Writer) Finalize() error
- func (w *Writer) NewReader() (*Reader, error)
- func (w *Writer) SetStartTime(t time.Time) error
- func (w *Writer) SetTag(key string, value []byte) error
- func (w *Writer) Sync() error
- func (w *Writer) WriteFrame(id TrackID, fr Frame) error
Examples ¶
Constants ¶
const ( TagStartTime = "gmc.start_time_unix_ns" TagLocation = "gmc.location" )
Well-known tag keys. The "gmc." prefix is reserved.
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 ¶
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.
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 ¶
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) FilePrivate ¶
FilePrivate returns the immutable file-level private data.
func (*Reader) Finalized ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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) 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.
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 ¶
TrackFrame is one frame delivered by Follow, tagged with its track.
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.
type TrackSummary ¶
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 ¶
AddTrack registers a new track. Must be called before writing any frame of that track.
func (*Writer) Close ¶
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 ¶
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 ¶
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 ¶
SetStartTime stores the absolute wall-clock time of pts 0 (all tracks share the same time origin) under the TagStartTime key.
Source Files
¶
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. |