Last active
August 29, 2015 14:06
-
-
Save gwik/e87162fdfb8449748afd to your computer and use it in GitHub Desktop.
Revisions
-
gwik revised this gist
Sep 18, 2014 . 1 changed file with 21 additions and 31 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -7,7 +7,7 @@ import ( "fmt" "io" "os" "strings" ) const ( @@ -67,7 +67,7 @@ func (ph *pageHeader) ReadHeader(p []byte) error { } func (ph *pageHeader) ReadSegments(p []byte) error { if len(p) < int(ph.count) { return errors.New("Invalid buffer size.") } for i := uint8(0); i < ph.count; i++ { @@ -123,7 +123,7 @@ func findCodecInfo(hdr []byte) *codecInfo { return nil } // peek looks at the first page for every streams and extract codec info. func peek(in io.ReadSeeker) ([]streamInfo, error) { buf := make([]byte, 256) hbuf := buf[:27] @@ -173,6 +173,7 @@ func peek(in io.ReadSeeker) ([]streamInfo, error) { } } // returns mimeType as defined in RFC5334 func mimeType(streams []streamInfo) string { flags := 0 for _, stream := range streams { @@ -190,47 +191,36 @@ func mimeType(streams []streamInfo) string { // returns mimeType + codecs attribute as defined in RFC5334 func contentType(streams []streamInfo) string { flags := 0 mime := "application/ogg" codecs := make([]string, 0, len(streams)) for _, stream := range streams { flags |= stream.codec.flag codecs = append(codecs, stream.codec.name) } if flags&flagCodecVideo > 0 { mime = "video/ogg" } else if flags&flagCodecAudio > 0 { mime = "audio/ogg" } if len(codecs) > 0 { return fmt.Sprintf(`%s codecs="%s"`, mime, strings.Join(codecs, ", ")) } return mime } func main() { // curl -LO http://techslides.com/demos/sample-videos/small.ogv f, err := os.Open("small.ogv") if err != nil { fmt.Println(err) os.Exit(1) } // net/http.DetectContentType only read the first 512 bytes // it seems to work with this video but there is no warranty the // coded infos are within the first 512 bytes. buf := make([]byte, 512) _, err = io.ReadFull(f, buf) if err != nil { -
gwik renamed this gist
Sep 18, 2014 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
gwik created this gist
Sep 18, 2014 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,250 @@ package main import ( "bytes" "encoding/binary" "errors" "fmt" "io" "os" //"hash/crc32" ) const ( flagContinued = 0x01 flagFirst = 0x02 flagLast = 0x04 ) var capturePattern = []byte("OggS") // pageHeader starts with OggS type pageHeader struct { version uint8 // stream_structure_version (4) flags uint8 // header_type_flag (5) pos uint64 // absolute granule position (6-13) sn uint32 // stream serial number (14-17) seqNo uint32 // page sequence no (18-21) checksum uint32 // page checksum (22-25) count uint8 // page_segments (26) segments [256]uint8 // segment_table (27-n, n=page_segments+26) } func (ph *pageHeader) Len() int { return int(27 + ph.count) } func (ph *pageHeader) PayloadLen() int { l := 0 for i := uint8(0); i < ph.count; i++ { l += int(ph.segments[i]) } return l } // caller should ensure buf should be a least 27 bytes func (ph *pageHeader) ReadHeader(p []byte) error { if len(p) < 27 { return errors.New("Invalid buffer size.") } if !bytes.Equal(p[:4], capturePattern) { return errors.New("Capture pattern not found.") } ph.version = p[4] if ph.version != 0 { return fmt.Errorf("Unsupported version: %d", ph.version) } ph.flags = p[5] ph.pos = binary.LittleEndian.Uint64(p[6:]) ph.sn = binary.LittleEndian.Uint32(p[14:]) ph.seqNo = binary.LittleEndian.Uint32(p[18:]) ph.checksum = binary.LittleEndian.Uint32(p[22:]) ph.count = p[26] if ph.count > 255 { return fmt.Errorf("Invalid packet count %d.", ph.count) } return nil } func (ph *pageHeader) ReadSegments(p []byte) error { if len(p) < ph.count { return errors.New("Invalid buffer size.") } for i := uint8(0); i < ph.count; i++ { ph.segments[i] = p[i] } return nil } const ( flagCodecVideo = 1 << iota flagCodecAudio = 1 << iota flagCodecText = 1 << iota ) type codecInfo struct { prefix []byte name string flag int } // http://wiki.xiph.org/index.php/MIMETypesCodecs var codecTable = []codecInfo{ {[]byte("CELT "), "celt", flagCodecAudio}, {[]byte("CMML\x00\x00\x00\x00"), "cmml", flagCodecText}, {[]byte("BBCD\x00"), "dirac", flagCodecVideo}, {[]byte("\177FLAC"), "flac", flagCodecAudio}, {[]byte("\213JNG\r\n\032\n"), "jng", flagCodecVideo}, {[]byte("\x80kate\x00\x00\x00"), "kate", flagCodecText}, {[]byte("OggMIDI\x00"), "midi", flagCodecText}, {[]byte("\212MNG\r\n\032\n"), "mng", flagCodecVideo}, {[]byte("OpusHead"), "opus", flagCodecAudio}, {[]byte("PCM "), "pcm", flagCodecAudio}, {[]byte("\211PNG\r\n\032\n"), "png", flagCodecVideo}, {[]byte("Speex "), "speex", flagCodecAudio}, {[]byte("\x80theora"), "theora", flagCodecVideo}, {[]byte("\x01vorbis"), "vorbis", flagCodecAudio}, {[]byte("YUV4MPEG"), "yuv4mpeg", flagCodecVideo}, } type streamInfo struct { sn uint32 codec *codecInfo } func findCodecInfo(hdr []byte) *codecInfo { hlen := len(hdr) for _, pte := range codecTable { plen := len(pte.prefix) if hlen > plen && bytes.Equal(hdr[:plen], pte.prefix) { return &pte } } return nil } // peek looks at the first pages for every streams and extract stream info. func peek(in io.ReadSeeker) ([]streamInfo, error) { buf := make([]byte, 256) hbuf := buf[:27] streams := make([]streamInfo, 0, 3) ph := new(pageHeader) for { offset := 0 _, err := io.ReadFull(in, hbuf) if err != nil { return streams, err } err = ph.ReadHeader(hbuf) if err != nil { return streams, err } segbuf := buf[:ph.count] _, err = io.ReadFull(in, segbuf) if err != nil { return streams, err } ph.ReadSegments(segbuf) if ph.flags&flagFirst < 1 { return streams, nil } offset += ph.PayloadLen() if ph.count > 0 { size := int(ph.segments[0]) _, err := io.ReadFull(in, buf[:size]) if err != nil { return streams, err } codec := findCodecInfo(buf) if codec != nil { streams = append(streams, streamInfo{ph.sn, codec}) } offset -= size } _, err = in.Seek(int64(offset), 1) if err != nil { return streams, err } } } func mimeType(streams []streamInfo) string { flags := 0 for _, stream := range streams { flags |= stream.codec.flag } if flags&flagCodecVideo > 0 { return "video/ogg" } if flags&flagCodecAudio > 0 { return "audio/ogg" } return "application/ogg" } // returns mimeType + codecs attribute as defined in RFC5334 func contentType(streams []streamInfo) string { flags := 0 for _, stream := range streams { flags |= stream.codec.flag } if flags&flagCodecVideo > 0 { ct := bytes.NewBufferString("video/ogg; codecs=\"") for _, stream := range streams { if stream.codec.flag&flagCodecVideo > 0 { ct.WriteString(stream.codec.name) break } } for _, stream := range streams { if stream.codec.flag&flagCodecAudio > 0 { ct.WriteString(", ") ct.WriteString(stream.codec.name) break } } ct.WriteRune('"') return ct.String() } if flags&flagCodecAudio > 0 { ct := bytes.NewBufferString("audio/ogg; codecs=\"") for _, stream := range streams { if stream.codec.flag&flagCodecAudio > 0 { ct.WriteString(stream.codec.name) break } } ct.WriteRune('"') return ct.String() } return "application/ogg" } func main() { f, err := os.Open("/Users/gwik/dev/go/src/camlistore.org/small.ogv") if err != nil { fmt.Println(err) os.Exit(1) } buf := make([]byte, 512) _, err = io.ReadFull(f, buf) if err != nil { os.Exit(128) } streams, err := peek(bytes.NewReader(buf)) if err != nil { fmt.Println(err) os.Exit(2) } for _, stream := range streams { fmt.Printf("stream sn=%d codec=%s\n", stream.sn, stream.codec.name) } fmt.Println(mimeType(streams)) fmt.Println(contentType(streams)) }