1 /*
2 * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd.
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
16 #include <arpa/inet.h>
17 #include <chrono>
18 #include <fstream>
19 #include <functional>
20 #include <getopt.h>
21 #include <netinet/in.h>
22 #include <signal.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/socket.h>
27 #include <thread>
28 #include <unistd.h>
29 #include "codec_factory.h"
30 #include "common/sharing_log.h"
31 #include "frame.h"
32 #include "media_frame_pipeline.h"
33 #include "media_player.h"
34 #include "rtp_def.h"
35 #include "rtp_factory.h"
36
37 using namespace OHOS::Sharing;
38
39 int gType = -1; // 0 for G.711
40 char *gInFile = nullptr;
41 int gPort = 0;
42 #define BUFF_SIZE 10240
43
ShowUsage(char * exe)44 void ShowUsage(char *exe)
45 {
46 SHARING_LOGD("usage:\n%{public}s -i <in file> [-p <local port>]", exe);
47 SHARING_LOGD("\t-t 0: play g711 alaw file, 1: play g711 RTP stream");
48 SHARING_LOGD("\t-i in file");
49 SHARING_LOGD("\t-p local rtp port");
50 }
51
ParseParam(int argc,char * argv[])52 int ParseParam(int argc, char *argv[])
53 {
54 int ret;
55
56 while ((ret = getopt(argc, argv, ":t:i:p:")) != -1) {
57 switch (ret) {
58 case ('t'):
59 gType = atoi(optarg);
60 break;
61 case ('i'):
62 gInFile = optarg;
63 break;
64 case ('p'):
65 gPort = atoi(optarg);
66 break;
67 case ':':
68 SHARING_LOGD("option [-%c] requires an argument.", static_cast<char>(optopt));
69 break;
70 case '?':
71 SHARING_LOGD("unknown option: %c.", static_cast<char>(optopt));
72 break;
73 default:
74 break;
75 }
76 }
77
78 if (!((gType == 0 && gInFile != nullptr) || (gType == 1 && gPort > 0))) {
79 SHARING_LOGD("param error");
80 ShowUsage(argv[0]);
81 return -1;
82 }
83
84 return 0;
85 }
86
87 class RawDataReceiver : public FrameDestination {
88 public:
RawDataReceiver(const std::shared_ptr<MediaPlayer> & player)89 RawDataReceiver(const std::shared_ptr<MediaPlayer> &player) : player_(player) {}
90
91 ~RawDataReceiver() = default;
92
OnFrame(const Frame::Ptr & frame)93 void OnFrame(const Frame::Ptr &frame) override
94 {
95 player_->OnData((uint8_t *)frame->Data(), (size_t)frame->Size(), true);
96 SHARING_LOGD("recv decoded data(%p) len(%{public}d)", frame->Data(), frame->Size());
97 for (int i = 0; i < 12 && i < frame->Size(); i++) {
98 printf("%02x ", *(frame->Data() + i));
99 }
100 printf("\n");
101 }
102
103 private:
104 std::shared_ptr<MediaPlayer> player_;
105 };
106
InitMediaPlayer(int channels,int sampleRate)107 std::shared_ptr<MediaPlayer> InitMediaPlayer(int channels, int sampleRate)
108 {
109 std::shared_ptr<MediaPlayer> player = std::make_shared<MediaPlayer>();
110 player->SetAudioParameter(channels, sampleRate);
111 player->Prepare();
112 player->Play();
113 return player;
114 }
115
DecodeG711ByTime(char * data,int length)116 void DecodeG711ByTime(char *data, int length)
117 {
118 std::shared_ptr<AudioDecoder> decoder = CodecFactory::CreateAudioDecoder(CODEC_G711A);
119 if (!decoder) {
120 return;
121 }
122
123 decoder->Init();
124 // set audio paramters
125 int sampleRate = 44100;
126 int channel = 1;
127
128 std::shared_ptr<MediaPlayer> player = InitMediaPlayer(channel, sampleRate);
129
130 auto rawReceiver = std::make_shared<RawDataReceiver>(player);
131
132 decoder->AddAudioDestination(rawReceiver);
133 auto g711Frame = FrameImpl::Create();
134 g711Frame->codecId_ = CODEC_G711A;
135 char *p = data;
136
137 std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
138 std::chrono::microseconds interval = std::chrono::milliseconds(20); // 20 ms, 50 packets/second
139 int packSize = (sampleRate / 50) * channel;
140 SHARING_LOGD("sampleRate: %{public}d, channel: %{public}d, packSize per 20ms: %{public}d", sampleRate, channel,
141 packSize);
142 for (int i = 0; i < length / packSize; i++) {
143 SHARING_LOGD("for i(%{public}d)", i);
144 g711Frame->Clear();
145 g711Frame->Assign(p, packSize);
146 decoder->OnFrame(g711Frame);
147 p += packSize;
148 std::this_thread::sleep_until(start + interval * i);
149 }
150
151 SHARING_LOGD("decodeG711ByTime line(%{public}d).", __LINE__);
152 }
153
DecodeG711Immediately(char * data,int length)154 void DecodeG711Immediately(char *data, int length) {}
155
RecvUDP(const std::function<void (const char * buf,int len)> & cb)156 int RecvUDP(const std::function<void(const char *buf, int len)> &cb)
157 {
158 int servSocket, nbytes;
159 struct sockaddr_in servAddr, cliAddr;
160 int addrLen = sizeof(cliAddr);
161 char buffer[BUFF_SIZE];
162
163 if ((servSocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
164 perror("socket err");
165 return -1;
166 }
167
168 bzero(&servAddr, sizeof(servAddr));
169
170 servAddr.sin_family = AF_INET;
171 servAddr.sin_port = htons(gPort);
172 servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
173
174 if (bind(servSocket, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0) {
175 perror("bind err");
176 return -1;
177 }
178
179 while (1) {
180 if ((nbytes = recvfrom(servSocket, buffer, sizeof(buffer), 0, (struct sockaddr *)&cliAddr,
181 (socklen_t *)&addrLen)) < 0) {
182 perror("recvfrom err");
183 return -1;
184 }
185
186 SHARING_LOGD("\nRecv data(size:%{public}d) From %{public}s:%{public}d", nbytes, inet_ntoa(cliAddr.sin_addr),
187 ntohs(cliAddr.sin_port));
188
189 cb(buffer, nbytes);
190 memset(buffer, 0, BUFF_SIZE);
191 }
192 }
193
main(int argc,char * argv[])194 int main(int argc, char *argv[])
195 {
196 SHARING_LOGD("trace");
197 if (ParseParam(argc, argv) != 0) {
198 return -1;
199 }
200
201 // play local g711 file
202 if (gType == 0) {
203 std::fstream infile(gInFile, std::ios::in | std::ios_base::binary);
204 if (!infile.is_open()) {
205 SHARING_LOGD("failed to open file");
206 return -1;
207 }
208
209 infile.seekg(0, std::ios::end);
210 int size = infile.tellg();
211 infile.seekg(0, std::ios::beg);
212
213 char *content = new char[size];
214 infile.read(content, size);
215 SHARING_LOGD("size %{public}d.", size);
216 infile.close();
217
218 DecodeG711ByTime(content, size);
219 delete[] content;
220 } else if (gType == 1) {
221 // recv rtp g711 stream, decode rtp, play
222
223 std::shared_ptr<AudioDecoder> decoder = CodecFactory::CreateAudioDecoder(CODEC_G711A);
224 if (!decoder) {
225 return -1;
226 }
227
228 decoder->Init();
229 std::shared_ptr<MediaPlayer> player = InitMediaPlayer(2, 8000);
230 auto rawReceiver = std::make_shared<RawDataReceiver>(player);
231 decoder->AddAudioDestination(rawReceiver);
232
233 auto g711Unpack = RtpFactory::CreateRtpUnpack(RtpPlaylodParam{97, 8000, RtpPayloadStream::PCMA});
234 g711Unpack->SetOnRtpUnpack([=](uint32_t ssrc, const Frame::Ptr &frame) {
235 SHARING_LOGD("setOnRtpUnpack");
236 if (frame->GetTrackType() == TRACK_AUDIO) {
237 SHARING_LOGD("unpack G711 rtp: len: %{public}d dts: %{public}d.", frame->Size(), frame->Dts());
238 for (int i = 0; i < 0x12 && i < static_cast<int>(frame->Size()); i++) {
239 printf("%02x ", *(frame->Data() + i));
240 }
241 printf("\n");
242
243 decoder->OnFrame(frame);
244 }
245 });
246
247 RecvUDP([g711Unpack](const char *buf, int len) {
248 // print data (12B RTP header + 12B Payload)
249 for (int i = 0; i < 24 && i < len; i++) {
250 printf("%02x ", buf[i] & 0xff);
251 if (i == 11) {
252 printf("\n");
253 }
254 }
255 printf("\n");
256 g711Unpack->ParseRtp(buf, len);
257 });
258 }
259 }