• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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()189 AudioFormat WfdRtspM3Response::GetAudioCodecs()
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             continue;
202         } else if (format == "AAC") { // AAC
203             if (audioFormat0 == 1) {
204                 return AUDIO_48000_16_2;
205             }
206         } else if (format == "AC3") { // AC3
207             if (audioFormat0 == 1) {
208             }
209             continue;
210         }
211     }
212     return AUDIO_48000_8_2;
213 }
214 
GetCoupledSink()215 std::string WfdRtspM3Response::GetCoupledSink()
216 {
217     std::string value = GetCustomParam(WFD_PARAM_COUPLED_SINK);
218     return value;
219 }
220 
GetContentProtection()221 std::string WfdRtspM3Response::GetContentProtection()
222 {
223     std::string value = GetCustomParam(WFD_PARAM_CONTENT_PROTECTION);
224     return value;
225 }
226 
GetVideoFormatsByCea(int index)227 VideoFormat WfdRtspM3Response::GetVideoFormatsByCea(int index)
228 {
229     WfdCeaResolution res = static_cast<WfdCeaResolution>(index);
230     switch (res) {
231         case CEA_640_480_P60:
232             return VIDEO_640X480_60;
233         case CEA_1280_720_P30:
234             return VIDEO_1280X720_30;
235         case CEA_1280_720_P60:
236             return VIDEO_1280X720_60;
237         case CEA_1920_1080_P25:
238             return VIDEO_1920X1080_25;
239         case CEA_1920_1080_P30:
240             return VIDEO_1920X1080_30;
241         case CEA_1920_1080_P60:
242             return VIDEO_1920X1080_60;
243         default:
244             return VIDEO_640X480_60;
245     }
246 }
247 
GetVideoFormats()248 VideoFormat WfdRtspM3Response::GetVideoFormats()
249 {
250     // wfd_video_formats:
251     // 1 byte "native"
252     // 1 byte "preferred-display-mode-supported" 0 or 1
253     // one or more avc codec structures
254     //   1 byte profile
255     //   1 byte level
256     //   4 byte CEA mask
257     //   4 byte VESA mask
258     //   4 byte HH mask
259     //   1 byte latency
260     //   2 byte min-slice-slice
261     //   2 byte slice-enc-params
262     //   1 byte framerate-control-support
263     //   max-hres (none or 2 byte)
264     //   max-vres (none or 2 byte)
265     /**
266      * native:  2*2HEXDIG
267      * preferred-display-mode-supported: 2*2HEXDIG; 0-not supported, 1-supported, 2-255 reserved
268      * profile: 2*2HEXDIG, only one bit set
269      * level:   2*2HEXDIG, only one bit set
270      * CEA-Support: 8*8HEXDIG, 0-ignore
271      * VESA-Support:8*8HEXDIG, 0-ignore
272      * HH-Support:  8*8HEXDIG, 0-ignore
273      * latency:     2*2HEXDIG, decoder latency in units of 5 msecs
274      * min-slice-size: 4*4HEXDIG, number of macroblocks
275      * slice-enc-params: 4*4HEXDIG,
276      * frame-rate-control-support: 4*4HEXDIG
277      * max-hres: 4*4HEXDIG, "none" if preferred-display-mode-supported is 0
278      * max-vres: 4*4HEXDIG, "none" if preferred-display-mode-supported is 0
279      **/
280     std::string value = GetCustomParam(WFD_PARAM_VIDEO_FORMATS);
281     if (value.size() < MINIMAL_VIDEO_FORMAT_SIZE) {
282         return VIDEO_640X480_60;
283     }
284 
285     std::string head = value.substr(0, 5); // 5: fix offset
286     uint16_t native = 0;
287     uint16_t preferredDisplayMode = 0;
288     int index = 0;
289     std::string temp = "";
290     bool run = true;
291     std::stringstream ss(head);
292     ss >> std::hex >> native >> std::hex >> preferredDisplayMode;
293     value = value.substr(5); // 5: fix offset
294 
295     while (run) {
296         auto nPos = value.find_first_of(",", index + 1);
297         if (nPos != std::string::npos) {
298             index = nPos;
299             temp = value.substr(0, index);
300         } else {
301             temp = value.substr(index + 1);
302             run = false;
303         }
304         std::stringstream sss(temp);
305         WfdVideoFormatsInfo wfdVideoFormatsInfo;
306         wfdVideoFormatsInfo.native = native;
307         wfdVideoFormatsInfo.preferredDisplayMode = preferredDisplayMode;
308         sss >> std::hex >> wfdVideoFormatsInfo.profile >> std::hex >> wfdVideoFormatsInfo.level >> std::hex >>
309             wfdVideoFormatsInfo.ceaMask >> std::hex >> wfdVideoFormatsInfo.veaMask >> std::hex >>
310             wfdVideoFormatsInfo.hhMask >> std::hex >> wfdVideoFormatsInfo.latency >> std::hex >>
311             wfdVideoFormatsInfo.minSlice >> std::hex >> wfdVideoFormatsInfo.sliceEncParam >> std::hex >>
312             wfdVideoFormatsInfo.frameRateCtlSupport;
313         vWfdVideoFormatsInfo_.push_back(wfdVideoFormatsInfo);
314     }
315 
316     uint8_t tableSelection = vWfdVideoFormatsInfo_[0].native & 0x7;
317     index = vWfdVideoFormatsInfo_[0].native >> BIT_OFFSET_THREE;
318     switch (tableSelection) {
319         case 0:
320             return GetVideoFormatsByCea(index);
321         default:
322             return VIDEO_640X480_60;
323     }
324     return VIDEO_640X480_60;
325 }
326 
GetStandbyResumeCapability()327 std::string WfdRtspM3Response::GetStandbyResumeCapability()
328 {
329     std::string value = GetCustomParam(WFD_PARAM_STANDBY_RESUME);
330     return value;
331 }
332 
GetCustomParam(const std::string & key)333 std::string WfdRtspM3Response::GetCustomParam(const std::string &key)
334 {
335     auto it = std::find_if(params_.begin(), params_.end(),
336                            [=](std::pair<std::string, std::string> value) { return value.first == key; });
337     if (it != params_.end()) {
338         return it->second;
339     }
340 
341     return "";
342 }
343 
SetClientRtpPorts(int32_t port)344 void WfdRtspM4Request::SetClientRtpPorts(int32_t port)
345 {
346     std::stringstream ss;
347     ss << WFD_PARAM_RTP_PORTS << ":" << RTSP_SP << "RTP/AVP/UDP;unicast" << RTSP_SP << port << RTSP_SP << 0 << RTSP_SP
348        << "mode=play";
349     AddBodyItem(ss.str());
350 }
351 
SetAudioCodecs(AudioFormat format)352 void WfdRtspM4Request::SetAudioCodecs(AudioFormat format)
353 {
354     std::stringstream ss;
355     ss << WFD_PARAM_AUDIO_CODECS << ":" << RTSP_SP;
356     switch (format) {
357         case AUDIO_44100_8_2:
358             ss << "LPCM" << RTSP_SP << "00000001 00";
359             break;
360         case AUDIO_44100_16_2:
361             ss << "LPCM" << RTSP_SP << "00000002 00";
362             break;
363         case AUDIO_48000_16_2:
364             ss << "AAC" << RTSP_SP << "00000001 00";
365             break;
366         default:
367             ss << "AAC" << RTSP_SP << "00000001 00";
368             break;
369     }
370     AddBodyItem(ss.str());
371 }
372 
SetPresentationUrl(const std::string & ip)373 void WfdRtspM4Request::SetPresentationUrl(const std::string &ip)
374 {
375     std::stringstream ss;
376     ss << WFD_PARAM_PRESENTATION_URL << ":" << RTSP_SP << "rtsp://" << ip.c_str() << "/wfd1.0/streamid=0 none";
377     AddBodyItem(ss.str());
378 }
379 
SetVideoFormats(const WfdVideoFormatsInfo & wfdVideoFormatsInfo,VideoFormat format)380 void WfdRtspM4Request::SetVideoFormats(const WfdVideoFormatsInfo &wfdVideoFormatsInfo, VideoFormat format)
381 {
382     std::stringstream ss;
383     uint32_t native = wfdVideoFormatsInfo.native;
384     uint32_t h264Profile = wfdVideoFormatsInfo.profile;
385     uint32_t h264Level = wfdVideoFormatsInfo.level;
386     uint32_t ceaResolutionIndex = 0;
387     uint32_t vesaResolutionIndex = 0;
388     uint32_t hhResolutionIndex = 0;
389     (void)ceaResolutionIndex;
390     (void)vesaResolutionIndex;
391     (void)hhResolutionIndex;
392     (void)h264Profile;
393     (void)h264Level;
394 
395     switch (format) {
396         case VIDEO_1920X1080_60:
397         case VIDEO_1920X1080_30:
398             native = (uint8_t)WfdResolutionType::RESOLUTION_CEA | (CEA_1920_1080_P30 << BIT_OFFSET_THREE);
399             ceaResolutionIndex = (1 << CEA_1920_1080_P30);
400             break;
401         case VIDEO_1920X1080_25:
402             native = (uint8_t)WfdResolutionType::RESOLUTION_CEA | (CEA_1920_1080_P25 << BIT_OFFSET_THREE);
403             ceaResolutionIndex = (1 << CEA_1920_1080_P25);
404             break;
405         case VIDEO_1280X720_30:
406             native = (uint8_t)WfdResolutionType::RESOLUTION_CEA | (CEA_1280_720_P30 << BIT_OFFSET_THREE);
407             ceaResolutionIndex = (1 << CEA_1280_720_P30);
408             break;
409         case VIDEO_1280X720_25:
410             native = (uint8_t)WfdResolutionType::RESOLUTION_CEA | (CEA_1280_720_P25 << BIT_OFFSET_THREE);
411             ceaResolutionIndex = (1 << CEA_1280_720_P25);
412             break;
413         case VIDEO_640X480_60:
414         default:
415             native =
416                 (uint8_t)WfdResolutionType::RESOLUTION_CEA | (WfdCeaResolution::CEA_640_480_P60 << BIT_OFFSET_THREE);
417             ceaResolutionIndex = (1 << CEA_640_480_P60);
418             break;
419     }
420 
421     ss << WFD_PARAM_VIDEO_FORMATS << ":" << RTSP_SP;
422     ss << std::setfill('0') << std::setw(BIT_OFFSET_TWO) << std::hex << native << RTSP_SP << "00" << RTSP_SP;
423     ss << std::setfill('0') << std::setw(BIT_OFFSET_TWO) << std::hex << h264Profile << RTSP_SP;
424     ss << std::setfill('0') << std::setw(BIT_OFFSET_TWO) << std::hex << h264Level << RTSP_SP;
425     ss << std::setfill('0') << std::setw(BIT_OFFSET_EIGHT) << std::hex << ceaResolutionIndex << RTSP_SP;
426     ss << std::setfill('0') << std::setw(BIT_OFFSET_EIGHT) << std::hex << vesaResolutionIndex << RTSP_SP;
427     ss << std::setfill('0') << std::setw(BIT_OFFSET_EIGHT) << std::hex << hhResolutionIndex << RTSP_SP;
428     ss << "00 0000 0000 00 none none";
429     AddBodyItem(ss.str());
430 }
431 
Parse(const std::string & request)432 RtspError WfdRtspM4Request::Parse(const std::string &request)
433 {
434     auto res = RtspRequest::Parse(request);
435     if (res.code != RtspErrorType::OK) {
436         return res;
437     }
438 
439     RtspCommon::SplitParameter(body_, params_);
440     return {};
441 }
442 
GetParameterValue(const std::string & param)443 std::string WfdRtspM4Request::GetParameterValue(const std::string &param)
444 {
445     auto it = std::find_if(params_.begin(), params_.end(),
446                            [=](std::pair<std::string, std::string> value) { return value.first == param; });
447     if (it != params_.end()) {
448         return it->second;
449     }
450 
451     return "";
452 }
453 
GetPresentationUrl()454 std::string WfdRtspM4Request::GetPresentationUrl()
455 {
456     std::string urls = GetParameterValue(WFD_PARAM_PRESENTATION_URL);
457     if (urls.empty()) {
458         return "";
459     }
460 
461     auto urlVec = RtspCommon::Split(urls, " ");
462     if (!urlVec.empty()) {
463         return urlVec[0];
464     }
465 
466     return "";
467 }
468 
GetRtpPort()469 int32_t WfdRtspM4Request::GetRtpPort()
470 {
471     auto ports = GetParameterValue(WFD_PARAM_RTP_PORTS);
472     auto resVec = RtspCommon::Split(ports, " ");
473     if (resVec.size() > 2) { // 2: fix offset
474         return atoi(resVec[1].c_str());
475     }
476 
477     return 0;
478 }
479 
SetTriggerMethod(const std::string & method)480 void WfdRtspM5Request::SetTriggerMethod(const std::string &method)
481 {
482     std::stringstream ss;
483     ss << WFD_PARAM_TRIGGER << ":" << RTSP_SP << method;
484 
485     body_.emplace_back(ss.str());
486 }
487 
GetTriggerMethod()488 std::string WfdRtspM5Request::GetTriggerMethod()
489 {
490     std::list<std::pair<std::string, std::string>> params;
491     RtspCommon::SplitParameter(body_, params);
492 
493     auto it = std::find_if(params.begin(), params.end(),
494                            [](std::pair<std::string, std::string> value) { return value.first == WFD_PARAM_TRIGGER; });
495     if (it != params.end()) {
496         return it->second;
497     }
498 
499     return {};
500 }
501 
GetClientPort()502 int32_t WfdRtspM6Response::GetClientPort()
503 {
504     auto transport = GetToken(RTSP_TOKEN_TRANSPORT);
505     if (transport.empty()) {
506         return 0;
507     }
508 
509     auto tsVec = RtspCommon::Split(transport, ";");
510     for (auto &item : tsVec) {
511         if (item.find("client_port=") != std::string::npos) {
512             auto val = item.substr(item.find('=') + 1);
513             return atoi(val.c_str());
514         }
515     }
516 
517     return 0;
518 }
519 
GetServerPort()520 int32_t WfdRtspM6Response::GetServerPort()
521 {
522     auto transport = GetToken(RTSP_TOKEN_TRANSPORT);
523     if (transport.empty()) {
524         return 0;
525     }
526 
527     auto tsVec = RtspCommon::Split(transport, ";");
528     for (auto &item : tsVec) {
529         if (item.find("server_port=") != std::string::npos) {
530             auto val = item.substr(item.find('=') + 1);
531             return atoi(val.c_str());
532         }
533     }
534 
535     return 0;
536 }
537 
SetClientPort(int port)538 void WfdRtspM6Response::SetClientPort(int port)
539 {
540     clientPort_ = port;
541 }
542 
SetServerPort(int port)543 void WfdRtspM6Response::SetServerPort(int port)
544 {
545     serverPort_ = port;
546 }
547 
StringifyEx()548 std::string WfdRtspM6Response::StringifyEx()
549 {
550     std::stringstream ss;
551     auto message = Stringify();
552     std::string temp = RTSP_CRLF;
553     auto nPos = message.find_last_of(RTSP_CRLF);
554     if (nPos != std::string::npos) {
555         message = message.substr(0, message.size() - temp.size());
556     }
557     ss << message << "Transport: RTP/AVP/UDP;unicast;client_port=" << clientPort_ << ";server_port=" << serverPort_
558        << RTSP_CRLF;
559     ss << RTSP_CRLF;
560     return ss.str();
561 }
562 
StringifyEx()563 std::string WfdRtspM7Response::StringifyEx()
564 {
565     std::stringstream ss;
566     auto message = Stringify();
567     std::string temp = RTSP_CRLF;
568     auto nPos = message.find_last_of(RTSP_CRLF);
569     if (nPos != std::string::npos) {
570         message = message.substr(0, message.size() - temp.size());
571     }
572     ss << message << "Range: npt=now-" << RTSP_CRLF;
573     ss << RTSP_CRLF;
574     return ss.str();
575 }
576 
577 } // namespace Sharing
578 } // namespace OHOS
579