Skip to content
This repository was archived by the owner on Mar 19, 2021. It is now read-only.

Commit 891d221

Browse files
committed
Stream audio from PS4
1 parent bb1c29d commit 891d221

1 file changed

Lines changed: 187 additions & 0 deletions

File tree

cli/src/stream.c

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
* This file is part of Chiaki.
3+
*
4+
* Chiaki is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* Chiaki is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
18+
#include <chiaki-cli.h>
19+
20+
#include <chiaki/session.h>
21+
#include <chiaki/base64.h>
22+
23+
#include <argp.h>
24+
#include <string.h>
25+
#include <unistd.h>
26+
27+
static char doc[] = "Request for PS4 stream.";
28+
29+
#define ARG_KEY_HOST 'h'
30+
#define ARG_KEY_REGISTKEY 'r'
31+
#define ARG_KEY_MORNING 'm'
32+
33+
static void audio_header_cb(ChiakiAudioHeader *header, void *user);
34+
static void audio_frame_cb(uint8_t *buf, size_t buf_size, void *user);
35+
static bool video_frame_cb(uint8_t *buf, size_t buf_size, void *user);
36+
37+
static struct argp_option options[] = {
38+
{ "host", ARG_KEY_HOST, "Host", 0, "Host to request stream from", 0 },
39+
{ "registkey", ARG_KEY_REGISTKEY, "RegistKey", 0, "PS4 registration key", 0 },
40+
{ "morning", ARG_KEY_MORNING, "Morning", 0, "PS4 morning encoded in base64", 0 },
41+
{ 0 }
42+
};
43+
44+
typedef struct arguments
45+
{
46+
const char *host;
47+
const char *registkey;
48+
const char *morning;
49+
} Arguments;
50+
51+
static int parse_opt(int key, char *arg, struct argp_state *state)
52+
{
53+
Arguments *arguments = state->input;
54+
55+
switch(key)
56+
{
57+
case ARG_KEY_HOST:
58+
arguments->host = arg;
59+
break;
60+
case ARG_KEY_REGISTKEY:
61+
arguments->registkey = arg;
62+
break;
63+
case ARG_KEY_MORNING:
64+
arguments->morning = arg;
65+
break;
66+
case ARGP_KEY_ARG:
67+
argp_usage(state);
68+
break;
69+
default:
70+
return ARGP_ERR_UNKNOWN;
71+
}
72+
73+
return 0;
74+
}
75+
76+
static struct argp argp = { options, parse_opt, 0, doc, 0, 0, 0 };
77+
78+
static void audio_header_cb(ChiakiAudioHeader *header, void *user)
79+
{
80+
uint8_t opus_id_head[0x13];
81+
memcpy(opus_id_head, "OpusHead", 8);
82+
opus_id_head[0x8] = 1; // version
83+
opus_id_head[0x9] = header->channels;
84+
uint16_t pre_skip = 3840;
85+
opus_id_head[0xa] = (uint8_t)(pre_skip & 0xff);
86+
opus_id_head[0xb] = (uint8_t)(pre_skip >> 8);
87+
opus_id_head[0xc] = (uint8_t)(header->rate & 0xff);
88+
opus_id_head[0xd] = (uint8_t)((header->rate >> 0x8) & 0xff);
89+
opus_id_head[0xe] = (uint8_t)((header->rate >> 0x10) & 0xff);
90+
opus_id_head[0xf] = (uint8_t)(header->rate >> 0x18);
91+
uint16_t output_gain = 0;
92+
opus_id_head[0x10] = (uint8_t)(output_gain & 0xff);
93+
opus_id_head[0x11] = (uint8_t)(output_gain >> 8);
94+
opus_id_head[0x12] = 0; // channel map
95+
audio_frame_cb(opus_id_head, sizeof(opus_id_head), user);
96+
97+
uint64_t pre_skip_ns = 0;
98+
uint8_t csd1[8] = { (uint8_t)(pre_skip_ns & 0xff), (uint8_t)((pre_skip_ns >> 0x8) & 0xff), (uint8_t)((pre_skip_ns >> 0x10) & 0xff), (uint8_t)((pre_skip_ns >> 0x18) & 0xff),
99+
(uint8_t)((pre_skip_ns >> 0x20) & 0xff), (uint8_t)((pre_skip_ns >> 0x28) & 0xff), (uint8_t)((pre_skip_ns >> 0x30) & 0xff), (uint8_t)(pre_skip_ns >> 0x38)};
100+
audio_frame_cb(csd1, sizeof(csd1), user);
101+
102+
uint64_t pre_roll_ns = 0;
103+
uint8_t csd2[8] = { (uint8_t)(pre_roll_ns & 0xff), (uint8_t)((pre_roll_ns >> 0x8) & 0xff), (uint8_t)((pre_roll_ns >> 0x10) & 0xff), (uint8_t)((pre_roll_ns >> 0x18) & 0xff),
104+
(uint8_t)((pre_roll_ns >> 0x20) & 0xff), (uint8_t)((pre_roll_ns >> 0x28) & 0xff), (uint8_t)((pre_roll_ns >> 0x30) & 0xff), (uint8_t)(pre_roll_ns >> 0x38)};
105+
audio_frame_cb(csd2, sizeof(csd2), user);
106+
}
107+
108+
static void audio_frame_cb(uint8_t *buf, size_t buf_size, void *user)
109+
{
110+
fwrite(buf, buf_size, 1, stderr);
111+
}
112+
113+
static bool video_frame_cb(uint8_t *buf, size_t buf_size, void *user)
114+
{
115+
fwrite(buf, buf_size, 1, stderr);
116+
}
117+
118+
CHIAKI_EXPORT int chiaki_cli_cmd_stream(ChiakiLog *log, int argc, char *argv[])
119+
{
120+
Arguments arguments = { 0 };
121+
error_t argp_r = argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &arguments);
122+
if(argp_r != 0)
123+
return 1;
124+
125+
if(!arguments.host)
126+
{
127+
fprintf(stderr, "No host specified, see --help.\n");
128+
return 1;
129+
}
130+
if(!arguments.registkey)
131+
{
132+
fprintf(stderr, "No registration key specified, see --help.\n");
133+
return 1;
134+
}
135+
if(!arguments.morning)
136+
{
137+
fprintf(stderr, "No morning specified, see --help.\n");
138+
return 1;
139+
}
140+
if(strlen(arguments.registkey) > 8)
141+
{
142+
fprintf(stderr, "Given registkey is too long.\n");
143+
return 1;
144+
}
145+
146+
ChiakiConnectInfo chiaki_connect_info;
147+
size_t morning_size = sizeof(chiaki_connect_info.morning);
148+
uint8_t morning[morning_size];
149+
chiaki_base64_decode(arguments.morning, strlen(arguments.morning), morning, &morning_size);
150+
memcpy(chiaki_connect_info.morning, morning, morning_size);
151+
chiaki_connect_info.host = arguments.host;
152+
memcpy(chiaki_connect_info.regist_key, arguments.registkey, sizeof(chiaki_connect_info.regist_key));
153+
154+
ChiakiConnectVideoProfile video_profile;
155+
video_profile.width = 640;
156+
video_profile.height = 360;
157+
video_profile.max_fps = 30;
158+
video_profile.bitrate = 128;
159+
chiaki_connect_info.video_profile = video_profile;
160+
161+
ChiakiSession session;
162+
ChiakiErrorCode err;
163+
err = chiaki_session_init(&session, &chiaki_connect_info, NULL);
164+
if(err != CHIAKI_ERR_SUCCESS)
165+
return 2;
166+
167+
ChiakiAudioSink audio_sink;
168+
audio_sink.header_cb = audio_header_cb;
169+
audio_sink.frame_cb = audio_frame_cb;
170+
chiaki_session_set_audio_sink(&session, &audio_sink);
171+
172+
// Video seems to work fine for now, commenting this so it doesn't
173+
// interfere with my attempts to get audio working.
174+
// chiaki_session_set_video_sample_cb(&session, video_frame_cb, NULL);
175+
176+
err = chiaki_session_start(&session);
177+
if(err != CHIAKI_ERR_SUCCESS)
178+
{
179+
chiaki_session_fini(&session);
180+
return 2;
181+
}
182+
183+
// For testing purpose. Quit after 20 secs.
184+
sleep(20);
185+
chiaki_session_fini(&session);
186+
return 0;
187+
}

0 commit comments

Comments
 (0)