|
- package icecast
-
- import (
- "bytes"
- "fmt"
- "io"
- "strconv"
- "strings"
- )
-
- type icyMetadata struct {
- StreamTitle string
- }
-
- type icyReader struct {
- r io.Reader
- metaInt int
- audioLeft int
- onMetadata func(icyMetadata)
- }
-
- func newICYReader(r io.Reader, metaInt int, onMetadata func(icyMetadata)) io.Reader {
- if r == nil || metaInt <= 0 {
- return r
- }
- return &icyReader{
- r: r,
- metaInt: metaInt,
- audioLeft: metaInt,
- onMetadata: onMetadata,
- }
- }
-
- func (r *icyReader) Read(p []byte) (int, error) {
- if len(p) == 0 {
- return 0, nil
- }
- for {
- if r.audioLeft == 0 {
- if err := r.readMetadataBlock(); err != nil {
- return 0, err
- }
- r.audioLeft = r.metaInt
- continue
- }
- want := len(p)
- if want > r.audioLeft {
- want = r.audioLeft
- }
- n, err := r.r.Read(p[:want])
- if n > 0 {
- r.audioLeft -= n
- return n, nil
- }
- if err != nil {
- return 0, err
- }
- }
- }
-
- func (r *icyReader) readMetadataBlock() error {
- var lenBuf [1]byte
- if _, err := io.ReadFull(r.r, lenBuf[:]); err != nil {
- return err
- }
- blockLen := int(lenBuf[0]) * 16
- if blockLen == 0 {
- return nil
- }
- block := make([]byte, blockLen)
- if _, err := io.ReadFull(r.r, block); err != nil {
- return err
- }
- if r.onMetadata != nil {
- r.onMetadata(parseICYMetadata(block))
- }
- return nil
- }
-
- func parseICYMetadata(block []byte) icyMetadata {
- raw := strings.TrimRight(string(bytes.Trim(block, "\x00")), "\x00")
- meta := icyMetadata{}
- for _, field := range strings.Split(raw, ";") {
- field = strings.TrimSpace(field)
- if !strings.HasPrefix(field, "StreamTitle=") {
- continue
- }
- v := strings.TrimPrefix(field, "StreamTitle=")
- v = strings.TrimSpace(v)
- if len(v) >= 2 && ((v[0] == '\'' && v[len(v)-1] == '\'') || (v[0] == '"' && v[len(v)-1] == '"')) {
- v = v[1 : len(v)-1]
- }
- meta.StreamTitle = v
- break
- }
- return meta
- }
-
- func parseICYMetaInt(raw string) (int, error) {
- raw = strings.TrimSpace(raw)
- if raw == "" {
- return 0, nil
- }
- n, err := strconv.Atoi(raw)
- if err != nil || n < 0 {
- return 0, fmt.Errorf("invalid icy-metaint: %q", raw)
- }
- return n, nil
- }
|