Skip to content

Commit 68c5690

Browse files
Galileyvishen
authored andcommitted
Added transcoded command
1 parent 5fa2cbb commit 68c5690

File tree

3 files changed

+192
-0
lines changed

3 files changed

+192
-0
lines changed

application/application.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,3 +1073,124 @@ func (a *Application) sendAndWaitMediaRecv(payload cast.Payload) (*pb.CastMessag
10731073
}
10741074
return a.sendAndWait(payload, defaultSender, a.application.TransportId, namespaceMedia)
10751075
}
1076+
1077+
func (a *Application) startTranscodingServer(command string) error {
1078+
if a.httpServer != nil {
1079+
return nil
1080+
}
1081+
a.log("trying to find available port to start transcoding server on")
1082+
1083+
listener, err := net.Listen("tcp", ":0")
1084+
if err != nil {
1085+
return errors.Wrap(err, "unable to bind to local tcp address")
1086+
}
1087+
1088+
a.serverPort = listener.Addr().(*net.TCPAddr).Port
1089+
a.log("found available port :%d", a.serverPort)
1090+
1091+
a.httpServer = &http.Server{}
1092+
1093+
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
1094+
// Check to see if we have a 'filename' and if it is one of the ones that have
1095+
// already been validated and is useable.
1096+
filename := r.URL.Query().Get("media_file")
1097+
canServe := false
1098+
for _, fn := range a.mediaFilenames {
1099+
if fn == filename {
1100+
canServe = true
1101+
}
1102+
}
1103+
1104+
a.playedItems[filename] = PlayedItem{ContentID: filename, Started: time.Now().Unix()}
1105+
a.writePlayedItems()
1106+
1107+
a.log("canServe=%t, liveStreaming=%t, filename=%s", canServe, true, filename)
1108+
if canServe {
1109+
args := strings.Split(command, " ")
1110+
cmd := exec.Command(args[0], args[1:]...)
1111+
1112+
cmd.Stdout = w
1113+
if a.debug {
1114+
cmd.Stderr = os.Stderr
1115+
}
1116+
1117+
w.Header().Set("Access-Control-Allow-Origin", "*")
1118+
w.Header().Set("Transfer-Encoding", "chunked")
1119+
1120+
if err := cmd.Run(); err != nil {
1121+
log.WithField("package", "application").WithFields(logrus.Fields{
1122+
"filename": filename,
1123+
}).WithError(err).Error("error transcoding")
1124+
}
1125+
} else {
1126+
http.Error(w, "Invalid file", 400)
1127+
}
1128+
a.log("method=%s, headers=%v, reponse_headers=%v", r.Method, r.Header, w.Header())
1129+
pi := a.playedItems[filename]
1130+
1131+
// TODO(vishen): make this a pointer?
1132+
pi.Finished = time.Now().Unix()
1133+
a.playedItems[filename] = pi
1134+
a.writePlayedItems()
1135+
})
1136+
1137+
go func() {
1138+
a.log("media server listening on %d", a.serverPort)
1139+
if err := a.httpServer.Serve(listener); err != nil && err != http.ErrServerClosed {
1140+
log.WithField("package", "application").WithError(err).Fatal("error serving HTTP")
1141+
}
1142+
}()
1143+
1144+
return nil
1145+
}
1146+
1147+
func (a *Application) Transcode(command string, contentType string) error {
1148+
1149+
if command == "" || contentType == "" {
1150+
return errors.New("command and content-type flags needs to be set when transcoding")
1151+
}
1152+
1153+
filename := "pipe_output"
1154+
// Add the filename to the list of filenames that go-chromecast will serve.
1155+
a.mediaFilenames = append(a.mediaFilenames, filename)
1156+
1157+
localIP, err := a.getLocalIP()
1158+
if err != nil {
1159+
return err
1160+
}
1161+
a.log("local IP address: %s", localIP)
1162+
1163+
a.log("starting transcoding server...")
1164+
// Start server to serve the media
1165+
if err := a.startTranscodingServer(command); err != nil {
1166+
return errors.Wrap(err, "unable to start transcoding server")
1167+
}
1168+
a.log("started transcoding server")
1169+
1170+
// We can only set the content url after the server has started, otherwise we have
1171+
// no way to know the port used.
1172+
contentURL := fmt.Sprintf("http://%s:%d?media_file=%s", localIP, a.serverPort, filename)
1173+
1174+
if err := a.ensureIsDefaultMediaReceiver(); err != nil {
1175+
return err
1176+
}
1177+
1178+
// NOTE: This isn't concurrent safe, but it doesn't need to be at the moment!
1179+
a.MediaStart()
1180+
1181+
// Send the command to the chromecast
1182+
a.sendMediaRecv(&cast.LoadMediaCommand{
1183+
PayloadHeader: cast.LoadHeader,
1184+
CurrentTime: 0,
1185+
Autoplay: true,
1186+
Media: cast.MediaItem{
1187+
ContentId: contentURL,
1188+
StreamType: "BUFFERED",
1189+
ContentType: contentType,
1190+
},
1191+
})
1192+
1193+
// Wait until we have been notified that the media has finished playing
1194+
a.MediaWait()
1195+
return nil
1196+
}

cmd/transcode.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright © 2018 Jonathan Pentecost <[email protected]>
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package cmd
16+
17+
import (
18+
"fmt"
19+
20+
"github.com/sirupsen/logrus"
21+
"github.com/spf13/cobra"
22+
"github.com/vishen/go-chromecast/ui"
23+
)
24+
25+
// transcodeCmd represents the transcode command
26+
var transcodeCmd = &cobra.Command{
27+
Use: "transcode",
28+
Short: "Transcode and play media on the chromecast",
29+
Long: `Transcode and play media on the chromecast. This will start a streaming server
30+
locally and serve the output of the transcoding operation to the chromecast.
31+
This command requires the program or script to write the media content to stdout.
32+
The transcoded media content-type is required as well`,
33+
RunE: func(cmd *cobra.Command, args []string) error {
34+
app, err := castApplication(cmd, args)
35+
if err != nil {
36+
fmt.Printf("unable to get cast application: %v\n", err)
37+
return nil
38+
}
39+
40+
contentType, _ := cmd.Flags().GetString("content-type")
41+
command, _ := cmd.Flags().GetString("command")
42+
43+
runWithUI, _ := cmd.Flags().GetBool("with-ui")
44+
if runWithUI {
45+
go func() {
46+
if err := app.Transcode(command, contentType); err != nil {
47+
logrus.WithError(err).Fatal("unable to load media")
48+
}
49+
}()
50+
51+
ccui, err := ui.NewUserInterface(app)
52+
if err != nil {
53+
logrus.WithError(err).Fatal("unable to prepare a new user-interface")
54+
}
55+
return ccui.Run()
56+
}
57+
58+
if err := app.Transcode(command, contentType); err != nil {
59+
fmt.Printf("unable to transcode media: %v\n", err)
60+
return nil
61+
}
62+
return nil
63+
},
64+
}
65+
66+
func init() {
67+
rootCmd.AddCommand(transcodeCmd)
68+
transcodeCmd.Flags().String("command", "", "command to use when transcoding")
69+
transcodeCmd.Flags().StringP("content-type", "c", "", "content-type to serve the media file as")
70+
}

testdata/helptext.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Available Commands:
2828
slideshow Play a slideshow of photos
2929
status Current chromecast status
3030
stop Stop casting
31+
transcode Transcode and play media on the chromecast
3132
tts text-to-speech
3233
ui Run the UI
3334
unmute Unmute the chromecast

0 commit comments

Comments
 (0)