Esempio n. 1
0
func (*generator) NewEncoder() gumble.AudioEncoder {
	e, _ := gopus.NewEncoder(gumble.AudioSampleRate, gumble.AudioChannels, gopus.Voip)
	e.SetBitrate(gopus.BitrateMaximum)
	return &Encoder{
		e,
	}
}
Esempio n. 2
0
func init() {
	var err error
	opusEncoder, err = gopus.NewEncoder(FrameRate, 1, gopus.Audio)
	if err != nil {
		fmt.Println("NewEncoder Error:", err)
		return
	}
	opusEncoder.SetBitrate(OpusBitrate)
	sequence = 0
	timestamp = 0
}
Esempio n. 3
0
// Connect connects to the server.
func (c *Client) Connect() error {
	if c.state != StateDisconnected {
		return errors.New("client is already connected")
	}
	encoder, err := gopus.NewEncoder(AudioSampleRate, 1, gopus.Voip)
	if err != nil {
		return err
	}
	encoder.SetBitrate(40000)
	encoder.SetVbr(false)
	c.audioSequence = 0
	c.audioTarget = nil

	c.connection, err = tls.DialWithDialer(&c.config.Dialer, "tcp", c.config.Address, &c.config.TLSConfig)
	if err != nil {
		return err
	}
	c.audioEncoder = encoder
	c.users = Users{}
	c.channels = Channels{}
	c.contextActions = ContextActions{}
	c.state = StateConnected

	// Channels and goroutines
	c.end = make(chan bool)
	go c.readRoutine()
	go c.pingRoutine()

	// Initial packets
	version := Version{
		release:   "gumble",
		os:        runtime.GOOS,
		osVersion: runtime.GOARCH,
	}
	version.setSemanticVersion(1, 2, 4)

	versionPacket := MumbleProto.Version{
		Version:   &version.version,
		Release:   &version.release,
		Os:        &version.os,
		OsVersion: &version.osVersion,
	}
	authenticationPacket := MumbleProto.Authenticate{
		Username: &c.config.Username,
		Password: &c.config.Password,
		Opus:     proto.Bool(true),
		Tokens:   c.config.Tokens,
	}
	c.Send(protoMessage{&versionPacket})
	c.Send(protoMessage{&authenticationPacket})
	return nil
}
Esempio n. 4
0
// SendPCM will receive on the provied channel encode
// received PCM data into Opus then send that to Discordgo
func SendPCM(v *discordgo.VoiceConnection, pcm <-chan []int16) {

	// make sure this only runs one instance at a time.
	mu.Lock()
	if sendpcm || pcm == nil {
		mu.Unlock()
		return
	}
	sendpcm = true
	mu.Unlock()

	defer func() { sendpcm = false }()

	var err error

	opusEncoder, err = gopus.NewEncoder(frameRate, channels, gopus.Audio)

	if err != nil {
		fmt.Println("NewEncoder Error:", err)
		return
	}

	for {

		// read pcm from chan, exit if channel is closed.
		recv, ok := <-pcm
		if !ok {
			fmt.Println("PCM Channel closed.")
			return
		}

		// try encoding pcm frame with Opus
		opus, err := opusEncoder.Encode(recv, frameSize, maxBytes)
		if err != nil {
			fmt.Println("Encoding Error:", err)
			return
		}

		if v.Ready == false || v.OpusSend == nil {
			fmt.Printf("Discordgo not ready for opus packets. %+v : %+v", v.Ready, v.OpusSend)
			return
		}
		// send encoded opus data to the sendOpus channel
		v.OpusSend <- opus
	}
}
Esempio n. 5
0
File: main.go Progetto: bwmarrin/dca
// very simple program that wraps ffmpeg and outputs raw opus data frames
// with a uint16 header for each frame with the frame length in bytes
func main() {

	//////////////////////////////////////////////////////////////////////////
	// BLOCK : Basic setup and validation
	//////////////////////////////////////////////////////////////////////////

	// If only one argument provided assume it's a filename.
	if len(os.Args) == 2 {
		InFile = os.Args[1]
	}

	// If reading from a file, verify it exists.
	if InFile != "pipe:0" {

		if _, err := os.Stat(InFile); os.IsNotExist(err) {
			fmt.Println("error: infile does not exist")
			flag.Usage()
			return
		}
	}

	// If reading from pipe, make sure pipe is open
	if InFile == "pipe:0" {
		fi, err := os.Stdin.Stat()
		if err != nil {
			fmt.Println(err)
			return
		}

		if (fi.Mode() & os.ModeCharDevice) == 0 {
		} else {
			fmt.Println("error: stdin is not a pipe.")
			flag.Usage()
			return
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// BLOCK : Create chans, buffers, and encoder for use
	//////////////////////////////////////////////////////////////////////////

	// create an opusEncoder to use
	OpusEncoder, err = gopus.NewEncoder(FrameRate, Channels, gopus.Audio)
	if err != nil {
		fmt.Println("NewEncoder Error:", err)
		return
	}

	// set opus encoding options
	//	OpusEncoder.SetVbr(true)                // bool

	if Bitrate < 1 || Bitrate > 512 {
		Bitrate = 64 // Set to Discord default
	}
	OpusEncoder.SetBitrate(Bitrate * 1000)

	switch Application {
	case "voip":
		OpusEncoder.SetApplication(gopus.Voip)
	case "audio":
		OpusEncoder.SetApplication(gopus.Audio)
	case "lowdelay":
		OpusEncoder.SetApplication(gopus.RestrictedLowDelay)
	default:
		OpusEncoder.SetApplication(gopus.Audio)
	}

	OutputChan = make(chan []byte, 10)
	EncodeChan = make(chan []int16, 10)

	if RawOutput == false {
		// Setup the metadata
		Metadata = MetadataStruct{
			Dca: &DCAMetadata{
				Version: FormatVersion,
				Tool: &DCAToolMetadata{
					Name:    "dca",
					Version: ProgramVersion,
					Url:     GitHubRepositoryURL,
					Author:  "bwmarrin",
				},
			},
			SongInfo: &SongMetadata{},
			Origin:   &OriginMetadata{},
			Opus: &OpusMetadata{
				Bitrate:     Bitrate * 1000,
				SampleRate:  FrameRate,
				Application: Application,
				FrameSize:   FrameSize,
				Channels:    Channels,
			},
			Extra: &ExtraMetadata{},
		}
		_ = Metadata

		// get ffprobe data
		if InFile != "pipe:0" {
			ffprobe := exec.Command("ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", InFile)
			ffprobe.Stdout = &CmdBuf

			err = ffprobe.Start()
			if err != nil {
				fmt.Println("RunStart Error:", err)
				return
			}

			err = ffprobe.Wait()
			if err != nil {
				fmt.Println("FFprobe Error:", err)
				return
			}

			err = json.Unmarshal(CmdBuf.Bytes(), &FFprobeData)
			if err != nil {
				fmt.Println("Erorr unmarshaling the FFprobe JSON:", err)
				return
			}

			bitrateInt, err := strconv.Atoi(FFprobeData.Format.Bitrate)
			if err != nil {
				fmt.Println("Could not convert bitrate to int:", err)
				return
			}

			Metadata.SongInfo = &SongMetadata{
				Title:    FFprobeData.Format.Tags.Title,
				Artist:   FFprobeData.Format.Tags.Artist,
				Album:    FFprobeData.Format.Tags.Album,
				Genre:    FFprobeData.Format.Tags.Genre,
				Comments: "", // change later?
			}

			Metadata.Origin = &OriginMetadata{
				Source:   "file",
				Bitrate:  bitrateInt,
				Channels: Channels,
				Encoding: FFprobeData.Format.FormatLongName,
			}

			CmdBuf.Reset()

			// get cover art
			cover := exec.Command("ffmpeg", "-loglevel", "0", "-i", InFile, "-f", "singlejpeg", "pipe:1")
			cover.Stdout = &CmdBuf

			err = cover.Start()
			if err != nil {
				fmt.Println("RunStart Error:", err)
				return
			}

			err = cover.Wait()
			if err == nil {
				buf := bytes.NewBufferString(CmdBuf.String())

				if CoverFormat == "png" {
					img, err := jpeg.Decode(buf)
					if err == nil { // silently drop it, no image
						err = png.Encode(&PngBuf, img)
						if err == nil {
							CoverImage = base64.StdEncoding.EncodeToString(PngBuf.Bytes())
						}
					}
				} else {
					CoverImage = base64.StdEncoding.EncodeToString(CmdBuf.Bytes())
				}

				Metadata.SongInfo.Cover = &CoverImage
			}

			CmdBuf.Reset()
			PngBuf.Reset()
		} else {
			Metadata.Origin = &OriginMetadata{
				Source:   "pipe",
				Channels: Channels,
				Encoding: "pcm16/s16le",
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// BLOCK : Start reader and writer workers
	//////////////////////////////////////////////////////////////////////////

	wg.Add(1)
	go reader()

	wg.Add(1)
	go encoder()

	wg.Add(1)
	go writer()

	// wait for above goroutines to finish, then exit.
	wg.Wait()
}