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 "wfd_message.h"
17 #include <iomanip>
18 #include <iostream>
19 #include <sstream>
20 #include <cstdio>
21 #include "common/sharing_log.h"
22 
23 namespace OHOS {
24 namespace Sharing {
25 constexpr int32_t BIT_OFFSET_TWO = 2;
26 constexpr int32_t BIT_OFFSET_THREE = 3;
27 constexpr int32_t BIT_OFFSET_EIGHT = 8;
28 
WfdRtspM1Response(int32_t cseq, int32_t status)29 WfdRtspM1Response::WfdRtspM1Response(int32_t cseq, int32_t status) : RtspResponseOptions(cseq, status)
30 {
31     std::stringstream ss;
32     ss << RTSP_METHOD_WFD << "," << RTSP_SP << RTSP_METHOD_SET_PARAMETER << "," << RTSP_SP << RTSP_METHOD_GET_PARAMETER;
33     SetPublicItems(ss.str());
34 }
35 
Parse(const std::string &response)36 RtspError WfdRtspM3Response::Parse(const std::string &response)
37 {
38     auto res = RtspResponse::Parse(response);
39     if (res.code != RtspErrorType::OK) {
40         return res;
41     }
42 
43     RtspCommon::SplitParameter(body_, params_);
44     return {};
45 }
46 
Stringify()47 std::string WfdRtspM3Response::Stringify()
48 {
49     std::stringstream body;
50     std::stringstream ss;
51 
52     for (auto &param : params_) {
53         body << param.first << ":" << RTSP_SP << param.second << RTSP_CRLF;
54     }
55 
56     ss << RTSP_TOKEN_CONTENT_TYPE << ":" << RTSP_SP << "text/parameters" << RTSP_CRLF;
57     ss << RTSP_TOKEN_CONTENT_LENGTH << ":" << RTSP_SP << body.str().length();
58 
59     ClearCustomHeader();
60     AddCustomHeader(ss.str());
61 
62     if (body.str().empty()) {
63         return RtspResponse::Stringify();
64     }
65 
66     return RtspResponse::Stringify() + body.str();
67 }
68 
SetVideoFormats(VideoFormat format)69 void WfdRtspM3Response::SetVideoFormats(VideoFormat format)
70 {
71     std::stringstream ss;
72     std::stringstream sinkVideoList;
73 
74     uint8_t native = 0;
75     uint8_t h264Profile = (1 << (uint8_t)WfdH264Profile::PROFILE_CHP);
76     uint8_t h264Level = (1 << (uint8_t)WfdH264Level::LEVEL_42);
77     uint32_t ceaResolutionIndex = 0;
78     uint32_t vesaResolutionIndex = 0;
79     uint32_t hhResolutionIndex = 0;
80 
81     switch (format) {
82         case VIDEO_1920X1080_30:
83             native = (uint8_t)WfdResolutionType::RESOLUTION_CEA | (CEA_1920_1080_P30 << BIT_OFFSET_THREE);
84             ceaResolutionIndex = (1 << CEA_1920_1080_P30);
85             break;
86         case VIDEO_1920X1080_25:
87             native = (uint8_t)WfdResolutionType::RESOLUTION_CEA | (CEA_1920_1080_P25 << BIT_OFFSET_THREE);
88             ceaResolutionIndex = (1 << CEA_1920_1080_P25);
89             break;
90         case VIDEO_1280X720_30:
91             native = (uint8_t)WfdResolutionType::RESOLUTION_CEA | (CEA_1280_720_P30 << BIT_OFFSET_THREE);
92             ceaResolutionIndex = (1 << CEA_1280_720_P30);
93             break;
94         case VIDEO_1280X720_25:
95             native = (uint8_t)WfdResolutionType::RESOLUTION_CEA | (CEA_1280_720_P25 << BIT_OFFSET_THREE);
96             ceaResolutionIndex = (1 << CEA_1280_720_P25);
97             break;
98         case VIDEO_640X480_60:
99         default:
100             native =
101                 (uint8_t)WfdResolutionType::RESOLUTION_CEA | (WfdCeaResolution::CEA_640_480_P60 << BIT_OFFSET_THREE);
102             ceaResolutionIndex = (1 << CEA_640_480_P60);
103             break;
104     }
105 
106     ss << std::setfill('0') << std::setw(BIT_OFFSET_TWO) << std::hex << (int32_t)native << RTSP_SP << "00" << RTSP_SP;
107     ss << std::setfill('0') << std::setw(BIT_OFFSET_TWO) << std::hex << (int32_t)h264Profile << RTSP_SP;
108     ss << std::setfill('0') << std::setw(BIT_OFFSET_TWO) << std::hex << (int32_t)h264Level << RTSP_SP;
109     ss << std::setfill('0') << std::setw(BIT_OFFSET_EIGHT) << std::hex << ceaResolutionIndex << RTSP_SP;
110     ss << std::setfill('0') << std::setw(BIT_OFFSET_EIGHT) << std::hex << vesaResolutionIndex << RTSP_SP;
111     ss << std::setfill('0') << std::setw(BIT_OFFSET_EIGHT) << std::hex << hhResolutionIndex << RTSP_SP;
112 
113     ss << "00 0000 0000 00 none none";
114 
115     params_.emplace_back(WFD_PARAM_VIDEO_FORMATS, ss.str());
116 }
117 
SetAudioCodecs(AudioFormat format)118 void WfdRtspM3Response::SetAudioCodecs(AudioFormat format)
119 {
120     std::stringstream ss;
121     switch (format) {
122         case AUDIO_44100_8_2:
123             ss << "LPCM" << RTSP_SP << "00000001 00";
124             break;
125         case AUDIO_44100_16_2:
126             ss << "LPCM" << RTSP_SP << "00000002 00";
127             break;
128         case AUDIO_48000_16_2:
129             ss << "AAC" << RTSP_SP << "00000001 00";
130             break;
131         default:
132             ss << "AAC" << RTSP_SP << "00000001 00";
133             break;
134     }
135 
136     params_.emplace_back(WFD_PARAM_AUDIO_CODECS, ss.str());
137 }
138 
SetClientRtpPorts(int32_t port)139 void WfdRtspM3Response::SetClientRtpPorts(int32_t port)
140 {
141     std::stringstream ss;
142     ss << "RTP/AVP/UDP;unicast" << RTSP_SP << port << RTSP_SP << 0 << RTSP_SP << "mode=play";
143     params_.emplace_back(WFD_PARAM_RTP_PORTS, ss.str());
144 }
145 
SetContentProtection(const std::string &value)146 void WfdRtspM3Response::SetContentProtection(const std::string &value)
147 {
148     params_.emplace_back(WFD_PARAM_CONTENT_PROTECTION, value);
149 }
150 
SetCoupledSink(const std::string &value)151 void WfdRtspM3Response::SetCoupledSink(const std::string &value)
152 {
153     params_.emplace_back(WFD_PARAM_COUPLED_SINK, value);
154 }
155 
SetUibcCapability(const std::string &value)156 void WfdRtspM3Response::SetUibcCapability(const std::string &value)
157 {
158     params_.emplace_back(WFD_PARAM_UIBC_CAPABILITY, value);
159 }
160 
SetStandbyResumeCapability(const std::string &value)161 void WfdRtspM3Response::SetStandbyResumeCapability(const std::string &value)
162 {
163     params_.emplace_back(WFD_PARAM_UIBC_CAPABILITY, value);
164 }
165 
SetCustomParam(const std::string &key, const std::string &value)166 void WfdRtspM3Response::SetCustomParam(const std::string &key, const std::string &value)
167 {
168     params_.emplace_back(key, value);
169 }
170 
GetClientRtpPorts()171 int32_t WfdRtspM3Response::GetClientRtpPorts()
172 {
173     int32_t port0 = -1;
174     int32_t port1 = -1;
175     std::string profile = "RTP/AVP/UDP;unicast";
176     std::string mode = "mode=play";
177     std::string value = GetCustomParam(WFD_PARAM_RTP_PORTS);
178     std::stringstream ss(value);
179     ss >> profile >> port0 >> port1 >> mode;
180     return port0;
181 }
182 
GetUibcCapability()183 std::string WfdRtspM3Response::GetUibcCapability()
184 {
185     std::string value = GetCustomParam(WFD_PARAM_UIBC_CAPABILITY);
186     return value;
187 }
188 
GetAudioCodecs(WfdAudioCodec &codec)189 AudioFormat WfdRtspM3Response::GetAudioCodecs(WfdAudioCodec &codec)
190 {
191     std::string value = GetCustomParam(WFD_PARAM_AUDIO_CODECS);
192     int32_t audioFormat0 = -1;
193     int32_t audioFormat1 = -1;
194     std::string format;
195     auto audioCaps = RtspCommon::Split(value, ", ");
196     for (size_t i = 0; i < audioCaps.size(); i++) {
197         std::string audioCap = audioCaps[i];
198         std::stringstream ss(audioCap);
199         ss >> format >> audioFormat0 >> audioFormat1;
200         if (format == "LPCM") { // LPCM
201             if (codec.codecId != CODEC_AAC && audioFormat0 > 1) {
202                 codec.codecId = CODEC_PCM;
203                 codec.format = AUDIO_48000_16_2;
204             }
205         } else if (format == "AAC") { // AAC
206             codec.codecId = CODEC_AAC;
207             codec.format = AUDIO_48000_16_2;
208         } else if (format == "AC3") { // AC3
209             if (audioFormat0 == 1) {
210             }
211             continue;
212         }
213     }
214 
215     return codec.format;
216 }
217 
GetCoupledSink()218 std::string WfdRtspM3Response::GetCoupledSink()
219 {
220     std::string value = GetCustomParam(WFD_PARAM_COUPLED_SINK);
221     return value;
222 }
223 
GetContentProtection()224 std::string WfdRtspM3Response::GetContentProtection()
225 {
226     std::string value = GetCustomParam(WFD_PARAM_CONTENT_PROTECTION);
227     return value;
228 }
229 
GetVideoFormatsByCea(int index)230 VideoFormat WfdRtspM3Response::GetVideoFormatsByCea(int index)
231 {
232     WfdCeaResolution res = static_cast<WfdCeaResolution>(index);
233     switch (res) {
234         case CEA_640_480_P60:
235             return VIDEO_640X480_60;
236         case CEA_1280_720_P30:
237             return VIDEO_1280X720_30;
238         case CEA_1280_720_P60:
239             return VIDEO_1280X720_60;
240         case CEA_1920_1080_P25:
241             return VIDEO_1920X1080_25;
242         case CEA_1920_1080_P30:
243             return VIDEO_1920X1080_30;
244         case CEA_1920_1080_P60:
245             return VIDEO_1920X1080_60;
246         default:
247             return VIDEO_640X480_60;
248     }
249 }
250 
GetVideoFormats()251 VideoFormat WfdRtspM3Response::GetVideoFormats()
252 {
253     // wfd_video_formats:
254     // 1 byte "native"
255     // 1 byte "preferred-display-mode-supported" 0 or 1
256     // one or more avc codec structures
257     //   1 byte profile
258     //   1 byte level
259     //   4 byte CEA mask
260     //   4 byte VESA mask
261     //   4 byte HH mask
262     //   1 byte latency
263     //   2 byte min-slice-slice
264     //   2 byte slice-enc-params
265     //   1 byte framerate-control-support
266     //   max-hres (none or 2 byte)
267     //   max-vres (none or 2 byte)
268     /**
269      * native:  2*2HEXDIG
270      * preferred-display-mode-supported: 2*2HEXDIG; 0-not supported, 1-supported, 2-255 reserved
271      * profile: 2*2HEXDIG, only one bit set
272      * level:   2*2HEXDIG, only one bit set
273      * CEA-Support: 8*8HEXDIG, 0-ignore
274      * VESA-Support:8*8HEXDIG, 0-ignore
275      * HH-Support:  8*8HEXDIG, 0-ignore
276      * latency:     2*2HEXDIG, decoder latency in units of 5 msecs
277      * min-slice-size: 4*4HEXDIG, number of macroblocks
278      * slice-enc-params: 4*4HEXDIG,
279      * frame-rate-control-support: 4*4HEXDIG
280      * max-hres: 4*4HEXDIG, "none" if preferred-display-mode-supported is 0
281      * max-vres: 4*4HEXDIG, "none" if preferred-display-mode-supported is 0
282      **/
283     std::string value = GetCustomParam(WFD_PARAM_VIDEO_FORMATS);
284     if (value.size() < MINIMAL_VIDEO_FORMAT_SIZE) {
285         return VIDEO_640X480_60;
286     }
287 
288     std::string head = value.substr(0, 5); // 5: fix offset
289     uint16_t native = 0;
290     uint16_t preferredDisplayMode = 0;
291     int index = 0;
292     std::string temp = "";
293     bool run = true;
294     std::stringstream ss(head);
295     ss >> std::hex >> native >> std::hex >> preferredDisplayMode;
296     value = value.substr(5); // 5: fix offset
297 
298     while (run) {
299         auto nPos = value.find_first_of(",", index + 1);
300         if (nPos != std::string::npos) {
301             index = nPos;
302             temp = value.substr(0, index);
303         } else {
304             temp = value.substr(index + 1);
305             run = false;
306         }
307         std::stringstream sss(temp);
308         WfdVideoFormatsInfo wfdVideoFormatsInfo;
309         wfdVideoFormatsInfo.native = native;
310         wfdVideoFormatsInfo.preferredDisplayMode = preferredDisplayMode;
311         sss >> std::hex >> wfdVideoFormatsInfo.profile >> std::hex >> wfdVideoFormatsInfo.level >> std::hex >>
312             wfdVideoFormatsInfo.ceaMask >> std::hex >> wfdVideoFormatsInfo.veaMask >> std::hex >>
313             wfdVideoFormatsInfo.hhMask >> std::hex >> wfdVideoFormatsInfo.latency >> std::hex >>
314             wfdVideoFormatsInfo.minSlice >> std::hex >> wfdVideoFormatsInfo.sliceEncParam >> std::hex >>
315             wfdVideoFormatsInfo.frameRateCtlSupport;
316         vWfdVideoFormatsInfo_.push_back(wfdVideoFormatsInfo);
317     }
318 
319     uint8_t tableSelection = vWfdVideoFormatsInfo_[0].native & 0x7;
320     index = vWfdVideoFormatsInfo_[0].native >> BIT_OFFSET_THREE;
321     switch (tableSelection) {
322         case 0:
323             return GetVideoFormatsByCea(index);
324         default:
325             return VIDEO_640X480_60;
326     }
327     return VIDEO_640X480_60;
328 }
329 
GetStandbyResumeCapability()330 std::string WfdRtspM3Response::GetStandbyResumeCapability()
331 {
332     std::string value = GetCustomParam(WFD_PARAM_STANDBY_RESUME);
333     return value;
334 }
335 
GetCustomParam(const std::string &key)336 std::string WfdRtspM3Response::GetCustomParam(const std::string &key)
337 {
338     auto it = std::find_if(params_.begin(), params_.end(),
339                            [=](std::pair<std::string, std::string> value) { return value.first == key; });
340     if (it != params_.end()) {
341         return it->second;
342     }
343 
344     return "";
345 }
346 
SetClientRtpPorts(int32_t port)347 void WfdRtspM4Request::SetClientRtpPorts(int32_t port)
348 {
349     std::stringstream ss;
350     ss << WFD_PARAM_RTP_PORTS << ":" << RTSP_SP << "RTP/AVP/UDP;unicast" << RTSP_SP << port << RTSP_SP << 0 << RTSP_SP
351        << "mode=play";
352     AddBodyItem(ss.str());
353 }
354 
SetAudioCodecs(WfdAudioCodec &codec)355 void WfdRtspM4Request::SetAudioCodecs(WfdAudioCodec &codec)
356 {
357     std::stringstream ss;
358     ss << WFD_PARAM_AUDIO_CODECS << ":" << RTSP_SP;
359 
360     switch (codec.codecId) {
361         case CODEC_PCM:
362             ss << "LPCM" << RTSP_SP << "00000002 00";
363             break;
364         case CODEC_AAC:
365             ss << "AAC" << RTSP_SP << "00000001 00";
366             break;
367         default:
368             ss << "AAC" << RTSP_SP << "00000001 00";
369             break;
370     }
371 
372     AddBodyItem(ss.str());
373 }
374 
SetPresentationUrl(const std::string &ip)375 void WfdRtspM4Request::SetPresentationUrl(const std::string &ip)
376 {
377     std::stringstream ss;
378     ss << WFD_PARAM_PRESENTATION_URL << ":" << RTSP_SP << "rtsp://" << ip.c_str() << "/wfd1.0/streamid=0 none";
379     AddBodyItem(ss.str());
380 }
381 
SetVideoFormats(const WfdVideoFormatsInfo &wfdVideoFormatsInfo, VideoFormat format)382 void WfdRtspM4Request::SetVideoFormats(const WfdVideoFormatsInfo &wfdVideoFormatsInfo, VideoFormat format)
383 {
384     std::stringstream ss;
385     uint32_t native = wfdVideoFormatsInfo.native;
386     uint32_t h264Profile = wfdVideoFormatsInfo.profile;
387     uint32_t h264Level = wfdVideoFormatsInfo.level;
388     uint32_t ceaResolutionIndex = 0;
389     uint32_t vesaResolutionIndex = 0;
390     uint32_t hhResolutionIndex = 0;
391     (void)ceaResolutionIndex;
392     (void)vesaResolutionIndex;
393     (void)hhResolutionIndex;
394     (void)h264Profile;
395     (void)h264Level;
396 
397     switch (format) {
398         case VIDEO_1920X1080_60:
399         case VIDEO_1920X1080_30:
400             native = (uint8_t)WfdResolutionType::RESOLUTION_CEA | (CEA_1920_1080_P30 << BIT_OFFSET_THREE);
401             ceaResolutionIndex = (1 << CEA_1920_1080_P30);
402             break;
403         case VIDEO_1920X1080_25:
404             native = (uint8_t)WfdResolutionType::RESOLUTION_CEA | (CEA_1920_1080_P25 << BIT_OFFSET_THREE);
405             ceaResolutionIndex = (1 << CEA_1920_1080_P25);
406             break;
407         case VIDEO_1280X720_30:
408             native = (uint8_t)WfdResolutionType::RESOLUTION_CEA | (CEA_1280_720_P30 << BIT_OFFSET_THREE);
409             ceaResolutionIndex = (1 << CEA_1280_720_P30);
410             break;
411         case VIDEO_1280X720_25:
412             native = (uint8_t)WfdResolutionType::RESOLUTION_CEA | (CEA_1280_720_P25 << BIT_OFFSET_THREE);
413             ceaResolutionIndex = (1 << CEA_1280_720_P25);
414             break;
415         case VIDEO_640X480_60:
416         default:
417             native =
418                 (uint8_t)WfdResolutionType::RESOLUTION_CEA | (WfdCeaResolution::CEA_640_480_P60 << BIT_OFFSET_THREE);
419             ceaResolutionIndex = (1 << CEA_640_480_P60);
420             break;
421     }
422 
423     ss << WFD_PARAM_VIDEO_FORMATS << ":" << RTSP_SP;
424     ss << std::setfill('0') << std::setw(BIT_OFFSET_TWO) << std::hex << native << RTSP_SP << "00" << RTSP_SP;
425     ss << std::setfill('0') << std::setw(BIT_OFFSET_TWO) << std::hex << h264Profile << RTSP_SP;
426     ss << std::setfill('0') << std::setw(BIT_OFFSET_TWO) << std::hex << h264Level << RTSP_SP;
427     ss << std::setfill('0') << std::setw(BIT_OFFSET_EIGHT) << std::hex << ceaResolutionIndex << RTSP_SP;
428     ss << std::setfill('0') << std::setw(BIT_OFFSET_EIGHT) << std::hex << vesaResolutionIndex << RTSP_SP;
429     ss << std::setfill('0') << std::setw(BIT_OFFSET_EIGHT) << std::hex << hhResolutionIndex << RTSP_SP;
430     ss << "00 0000 0000 00 none none";
431     AddBodyItem(ss.str());
432 }
433 
Parse(const std::string &request)434 RtspError WfdRtspM4Request::Parse(const std::string &request)
435 {
436     auto res = RtspRequest::Parse(request);
437     if (res.code != RtspErrorType::OK) {
438         return res;
439     }
440 
441     RtspCommon::SplitParameter(body_, params_);
442     return {};
443 }
444 
GetParameterValue(const std::string &param)445 std::string WfdRtspM4Request::GetParameterValue(const std::string &param)
446 {
447     auto it = std::find_if(params_.begin(), params_.end(),
448                            [=](std::pair<std::string, std::string> value) { return value.first == param; });
449     if (it != params_.end()) {
450         return it->second;
451     }
452 
453     return "";
454 }
455 
GetPresentationUrl()456 std::string WfdRtspM4Request::GetPresentationUrl()
457 {
458     std::string urls = GetParameterValue(WFD_PARAM_PRESENTATION_URL);
459     if (urls.empty()) {
460         return "";
461     }
462 
463     auto urlVec = RtspCommon::Split(urls, " ");
464     if (!urlVec.empty()) {
465         return urlVec[0];
466     }
467 
468     return "";
469 }
470 
GetRtpPort()471 int32_t WfdRtspM4Request::GetRtpPort()
472 {
473     auto ports = GetParameterValue(WFD_PARAM_RTP_PORTS);
474     auto resVec = RtspCommon::Split(ports, " ");
475     if (resVec.size() > 2) { // 2: fix offset
476         return atoi(resVec[1].c_str());
477     }
478 
479     return 0;
480 }
481 
SetTriggerMethod(const std::string &method)482 void WfdRtspM5Request::SetTriggerMethod(const std::string &method)
483 {
484     std::stringstream ss;
485     ss << WFD_PARAM_TRIGGER << ":" << RTSP_SP << method;
486 
487     body_.emplace_back(ss.str());
488 }
489 
GetTriggerMethod()490 std::string WfdRtspM5Request::GetTriggerMethod()
491 {
492     std::list<std::pair<std::string, std::string>> params;
493     RtspCommon::SplitParameter(body_, params);
494 
495     auto it = std::find_if(params.begin(), params.end(),
496                            [](std::pair<std::string, std::string> value) { return value.first == WFD_PARAM_TRIGGER; });
497     if (it != params.end()) {
498         return it->second;
499     }
500 
501     return {};
502 }
503 
GetClientPort()504 int32_t WfdRtspM6Response::GetClientPort()
505 {
506     auto transport = GetToken(RTSP_TOKEN_TRANSPORT);
507     if (transport.empty()) {
508         return 0;
509     }
510 
511     auto tsVec = RtspCommon::Split(transport, ";");
512     for (auto &item : tsVec) {
513         if (item.find("client_port=") != std::string::npos) {
514             auto val = item.substr(item.find('=') + 1);
515             return atoi(val.c_str());
516         }
517     }
518 
519     return 0;
520 }
521 
GetServerPort()522 int32_t WfdRtspM6Response::GetServerPort()
523 {
524     auto transport = GetToken(RTSP_TOKEN_TRANSPORT);
525     if (transport.empty()) {
526         return 0;
527     }
528 
529     auto tsVec = RtspCommon::Split(transport, ";");
530     for (auto &item : tsVec) {
531         if (item.find("server_port=") != std::string::npos) {
532             auto val = item.substr(item.find('=') + 1);
533             return atoi(val.c_str());
534         }
535     }
536 
537     return 0;
538 }
539 
SetClientPort(int port)540 void WfdRtspM6Response::SetClientPort(int port)
541 {
542     clientPort_ = port;
543 }
544 
SetServerPort(int port)545 void WfdRtspM6Response::SetServerPort(int port)
546 {
547     serverPort_ = port;
548 }
549 
StringifyEx()550 std::string WfdRtspM6Response::StringifyEx()
551 {
552     std::stringstream ss;
553     auto message = Stringify();
554     std::string temp = RTSP_CRLF;
555     auto nPos = message.find_last_of(RTSP_CRLF);
556     if (nPos != std::string::npos) {
557         message = message.substr(0, message.size() - temp.size());
558     }
559     ss << message << "Transport: RTP/AVP/UDP;unicast;client_port=" << clientPort_ << ";server_port=" << serverPort_
560        << RTSP_CRLF;
561     ss << RTSP_CRLF;
562     return ss.str();
563 }
564 
StringifyEx()565 std::string WfdRtspM7Response::StringifyEx()
566 {
567     std::stringstream ss;
568     auto message = Stringify();
569     std::string temp = RTSP_CRLF;
570     auto nPos = message.find_last_of(RTSP_CRLF);
571     if (nPos != std::string::npos) {
572         message = message.substr(0, message.size() - temp.size());
573     }
574     ss << message << "Range: npt=now-" << RTSP_CRLF;
575     ss << RTSP_CRLF;
576     return ss.str();
577 }
578 
579 } // namespace Sharing
580 } // namespace OHOS
581