winlin

fix #169, support default values for transcode. 2.0.180

@@ -344,6 +344,7 @@ Remark: @@ -344,6 +344,7 @@ Remark:
344 344
345 ### SRS 2.0 history 345 ### SRS 2.0 history
346 346
  347 +* v2.0, 2015-07-21, for [#169](https://github.com/simple-rtmp-server/srs/issues/169) support default values for transcode. 2.0.180
347 * v2.0, 2015-07-21, fix [#435](https://github.com/simple-rtmp-server/srs/issues/435) add pageUrl for HTTP callback on_play. 348 * v2.0, 2015-07-21, fix [#435](https://github.com/simple-rtmp-server/srs/issues/435) add pageUrl for HTTP callback on_play.
348 * v2.0, 2015-07-20, refine the hls, ignore packet when no sequence header. 2.0.179 349 * v2.0, 2015-07-20, refine the hls, ignore packet when no sequence header. 2.0.179
349 * v2.0, 2015-07-16, for [#441](https://github.com/simple-rtmp-server/srs/issues/441) use 30s timeout for first msg. 2.0.178 350 * v2.0, 2015-07-16, for [#441](https://github.com/simple-rtmp-server/srs/issues/441) use 30s timeout for first msg. 2.0.178
@@ -13,19 +13,12 @@ vhost __defaultVhost__ { @@ -13,19 +13,12 @@ vhost __defaultVhost__ {
13 vfilter { 13 vfilter {
14 } 14 }
15 vcodec libx264; 15 vcodec libx264;
16 - vbitrate 500;  
17 - vfps 25;  
18 - vwidth 768;  
19 - vheight 320;  
20 - vthreads 12; 16 + vthreads 4;
21 vprofile main; 17 vprofile main;
22 vpreset medium; 18 vpreset medium;
23 vparams { 19 vparams {
24 } 20 }
25 acodec libfdk_aac; 21 acodec libfdk_aac;
26 - abitrate 70;  
27 - asample_rate 44100;  
28 - achannels 2;  
29 aparams { 22 aparams {
30 } 23 }
31 output rtmp://127.0.0.1:[port]/[app]?vhost=[vhost]/[stream]_[engine]; 24 output rtmp://127.0.0.1:[port]/[app]?vhost=[vhost]/[stream]_[engine];
@@ -918,14 +918,23 @@ vhost example.transcode.srs.com { @@ -918,14 +918,23 @@ vhost example.transcode.srs.com {
918 # vn: disable video output. 918 # vn: disable video output.
919 vcodec libx264; 919 vcodec libx264;
920 # video bitrate, in kbps 920 # video bitrate, in kbps
  921 + # @remark 0 to use source video bitrate.
  922 + # default: 0
921 vbitrate 1500; 923 vbitrate 1500;
922 # video framerate. 924 # video framerate.
  925 + # @remark 0 to use source video fps.
  926 + # default: 0
923 vfps 25; 927 vfps 25;
924 # video width, must be even numbers. 928 # video width, must be even numbers.
  929 + # @remark 0 to use source video width.
  930 + # default: 0
925 vwidth 768; 931 vwidth 768;
926 # video height, must be even numbers. 932 # video height, must be even numbers.
  933 + # @remark 0 to use source video height.
  934 + # default: 0
927 vheight 320; 935 vheight 320;
928 # the max threads for ffmpeg to used. 936 # the max threads for ffmpeg to used.
  937 + # default: 1
929 vthreads 12; 938 vthreads 12;
930 # x264 profile, @see x264 -help, can be: 939 # x264 profile, @see x264 -help, can be:
931 # high,main,baseline 940 # high,main,baseline
@@ -950,17 +959,24 @@ vhost example.transcode.srs.com { @@ -950,17 +959,24 @@ vhost example.transcode.srs.com {
950 # an: disable audio output. 959 # an: disable audio output.
951 acodec libfdk_aac; 960 acodec libfdk_aac;
952 # audio bitrate, in kbps. [16, 72] for libfdk_aac. 961 # audio bitrate, in kbps. [16, 72] for libfdk_aac.
  962 + # @remark 0 to use source audio bitrate.
  963 + # default: 0
953 abitrate 70; 964 abitrate 70;
954 # audio sample rate. for flv/rtmp, it must be: 965 # audio sample rate. for flv/rtmp, it must be:
955 # 44100,22050,11025,5512 966 # 44100,22050,11025,5512
  967 + # @remark 0 to use source audio sample rate.
  968 + # default: 0
956 asample_rate 44100; 969 asample_rate 44100;
957 # audio channel, 1 for mono, 2 for stereo. 970 # audio channel, 1 for mono, 2 for stereo.
  971 + # @remark 0 to use source audio channels.
  972 + # default: 0
958 achannels 2; 973 achannels 2;
959 # other ffmpeg audio params 974 # other ffmpeg audio params
960 aparams { 975 aparams {
961 # audio params, @see: http://ffmpeg.org/ffmpeg-codecs.html#Audio-Encoders 976 # audio params, @see: http://ffmpeg.org/ffmpeg-codecs.html#Audio-Encoders
962 # @remark SRS supported aac profile for HLS is: aac_low, aac_he, aac_he_v2 977 # @remark SRS supported aac profile for HLS is: aac_low, aac_he, aac_he_v2
963 profile:a aac_low; 978 profile:a aac_low;
  979 + bsf:a aac_adtstoasc;
964 } 980 }
965 # output format, can be: 981 # output format, can be:
966 # off, do not specifies the format, ffmpeg will guess it. 982 # off, do not specifies the format, ffmpeg will guess it.
@@ -20,9 +20,6 @@ vhost __defaultVhost__ { @@ -20,9 +20,6 @@ vhost __defaultVhost__ {
20 enabled on; 20 enabled on;
21 vcodec copy; 21 vcodec copy;
22 acodec libfdk_aac; 22 acodec libfdk_aac;
23 - abitrate 45;  
24 - asample_rate 44100;  
25 - achannels 2;  
26 aparams { 23 aparams {
27 } 24 }
28 output rtmp://127.0.0.1:[port]/[app]?vhost=[vhost]/[stream]_[engine]; 25 output rtmp://127.0.0.1:[port]/[app]?vhost=[vhost]/[stream]_[engine];
@@ -2976,13 +2976,15 @@ string SrsConfig::get_engine_vcodec(SrsConfDirective* engine) @@ -2976,13 +2976,15 @@ string SrsConfig::get_engine_vcodec(SrsConfDirective* engine)
2976 2976
2977 int SrsConfig::get_engine_vbitrate(SrsConfDirective* engine) 2977 int SrsConfig::get_engine_vbitrate(SrsConfDirective* engine)
2978 { 2978 {
  2979 + static int DEFAULT = 0;
  2980 +
2979 if (!engine) { 2981 if (!engine) {
2980 - return 0; 2982 + return DEFAULT;
2981 } 2983 }
2982 2984
2983 SrsConfDirective* conf = engine->get("vbitrate"); 2985 SrsConfDirective* conf = engine->get("vbitrate");
2984 - if (!conf) {  
2985 - return 0; 2986 + if (!conf || conf->arg0().empty()) {
  2987 + return DEFAULT;
2986 } 2988 }
2987 2989
2988 return ::atoi(conf->arg0().c_str()); 2990 return ::atoi(conf->arg0().c_str());
@@ -2990,13 +2992,15 @@ int SrsConfig::get_engine_vbitrate(SrsConfDirective* engine) @@ -2990,13 +2992,15 @@ int SrsConfig::get_engine_vbitrate(SrsConfDirective* engine)
2990 2992
2991 double SrsConfig::get_engine_vfps(SrsConfDirective* engine) 2993 double SrsConfig::get_engine_vfps(SrsConfDirective* engine)
2992 { 2994 {
  2995 + static double DEFAULT = 0;
  2996 +
2993 if (!engine) { 2997 if (!engine) {
2994 - return 0; 2998 + return DEFAULT;
2995 } 2999 }
2996 3000
2997 SrsConfDirective* conf = engine->get("vfps"); 3001 SrsConfDirective* conf = engine->get("vfps");
2998 - if (!conf) {  
2999 - return 0; 3002 + if (!conf || conf->arg0().empty()) {
  3003 + return DEFAULT;
3000 } 3004 }
3001 3005
3002 return ::atof(conf->arg0().c_str()); 3006 return ::atof(conf->arg0().c_str());
@@ -3004,13 +3008,15 @@ double SrsConfig::get_engine_vfps(SrsConfDirective* engine) @@ -3004,13 +3008,15 @@ double SrsConfig::get_engine_vfps(SrsConfDirective* engine)
3004 3008
3005 int SrsConfig::get_engine_vwidth(SrsConfDirective* engine) 3009 int SrsConfig::get_engine_vwidth(SrsConfDirective* engine)
3006 { 3010 {
  3011 + static int DEFAULT = 0;
  3012 +
3007 if (!engine) { 3013 if (!engine) {
3008 - return 0; 3014 + return DEFAULT;
3009 } 3015 }
3010 3016
3011 SrsConfDirective* conf = engine->get("vwidth"); 3017 SrsConfDirective* conf = engine->get("vwidth");
3012 - if (!conf) {  
3013 - return 0; 3018 + if (!conf || conf->arg0().empty()) {
  3019 + return DEFAULT;
3014 } 3020 }
3015 3021
3016 return ::atoi(conf->arg0().c_str()); 3022 return ::atoi(conf->arg0().c_str());
@@ -3018,13 +3024,15 @@ int SrsConfig::get_engine_vwidth(SrsConfDirective* engine) @@ -3018,13 +3024,15 @@ int SrsConfig::get_engine_vwidth(SrsConfDirective* engine)
3018 3024
3019 int SrsConfig::get_engine_vheight(SrsConfDirective* engine) 3025 int SrsConfig::get_engine_vheight(SrsConfDirective* engine)
3020 { 3026 {
  3027 + static int DEFAULT = 0;
  3028 +
3021 if (!engine) { 3029 if (!engine) {
3022 - return 0; 3030 + return DEFAULT;
3023 } 3031 }
3024 3032
3025 SrsConfDirective* conf = engine->get("vheight"); 3033 SrsConfDirective* conf = engine->get("vheight");
3026 - if (!conf) {  
3027 - return 0; 3034 + if (!conf || conf->arg0().empty()) {
  3035 + return DEFAULT;
3028 } 3036 }
3029 3037
3030 return ::atoi(conf->arg0().c_str()); 3038 return ::atoi(conf->arg0().c_str());
@@ -3032,13 +3040,15 @@ int SrsConfig::get_engine_vheight(SrsConfDirective* engine) @@ -3032,13 +3040,15 @@ int SrsConfig::get_engine_vheight(SrsConfDirective* engine)
3032 3040
3033 int SrsConfig::get_engine_vthreads(SrsConfDirective* engine) 3041 int SrsConfig::get_engine_vthreads(SrsConfDirective* engine)
3034 { 3042 {
  3043 + static int DEFAULT = 1;
  3044 +
3035 if (!engine) { 3045 if (!engine) {
3036 - return 0; 3046 + return DEFAULT;
3037 } 3047 }
3038 3048
3039 SrsConfDirective* conf = engine->get("vthreads"); 3049 SrsConfDirective* conf = engine->get("vthreads");
3040 - if (!conf) {  
3041 - return 0; 3050 + if (!conf || conf->arg0().empty()) {
  3051 + return DEFAULT;
3042 } 3052 }
3043 3053
3044 return ::atoi(conf->arg0().c_str()); 3054 return ::atoi(conf->arg0().c_str());
@@ -3116,13 +3126,15 @@ string SrsConfig::get_engine_acodec(SrsConfDirective* engine) @@ -3116,13 +3126,15 @@ string SrsConfig::get_engine_acodec(SrsConfDirective* engine)
3116 3126
3117 int SrsConfig::get_engine_abitrate(SrsConfDirective* engine) 3127 int SrsConfig::get_engine_abitrate(SrsConfDirective* engine)
3118 { 3128 {
  3129 + static int DEFAULT = 0;
  3130 +
3119 if (!engine) { 3131 if (!engine) {
3120 - return 0; 3132 + return DEFAULT;
3121 } 3133 }
3122 3134
3123 SrsConfDirective* conf = engine->get("abitrate"); 3135 SrsConfDirective* conf = engine->get("abitrate");
3124 - if (!conf) {  
3125 - return 0; 3136 + if (!conf || conf->arg0().empty()) {
  3137 + return DEFAULT;
3126 } 3138 }
3127 3139
3128 return ::atoi(conf->arg0().c_str()); 3140 return ::atoi(conf->arg0().c_str());
@@ -3130,13 +3142,15 @@ int SrsConfig::get_engine_abitrate(SrsConfDirective* engine) @@ -3130,13 +3142,15 @@ int SrsConfig::get_engine_abitrate(SrsConfDirective* engine)
3130 3142
3131 int SrsConfig::get_engine_asample_rate(SrsConfDirective* engine) 3143 int SrsConfig::get_engine_asample_rate(SrsConfDirective* engine)
3132 { 3144 {
  3145 + static int DEFAULT = 0;
  3146 +
3133 if (!engine) { 3147 if (!engine) {
3134 - return 0; 3148 + return DEFAULT;
3135 } 3149 }
3136 3150
3137 SrsConfDirective* conf = engine->get("asample_rate"); 3151 SrsConfDirective* conf = engine->get("asample_rate");
3138 - if (!conf) {  
3139 - return 0; 3152 + if (!conf || conf->arg0().empty()) {
  3153 + return DEFAULT;
3140 } 3154 }
3141 3155
3142 return ::atoi(conf->arg0().c_str()); 3156 return ::atoi(conf->arg0().c_str());
@@ -3144,13 +3158,15 @@ int SrsConfig::get_engine_asample_rate(SrsConfDirective* engine) @@ -3144,13 +3158,15 @@ int SrsConfig::get_engine_asample_rate(SrsConfDirective* engine)
3144 3158
3145 int SrsConfig::get_engine_achannels(SrsConfDirective* engine) 3159 int SrsConfig::get_engine_achannels(SrsConfDirective* engine)
3146 { 3160 {
  3161 + static int DEFAULT = 0;
  3162 +
3147 if (!engine) { 3163 if (!engine) {
3148 - return 0; 3164 + return DEFAULT;
3149 } 3165 }
3150 3166
3151 SrsConfDirective* conf = engine->get("achannels"); 3167 SrsConfDirective* conf = engine->get("achannels");
3152 - if (!conf) {  
3153 - return 0; 3168 + if (!conf || conf->arg0().empty()) {
  3169 + return DEFAULT;
3154 } 3170 }
3155 3171
3156 return ::atoi(conf->arg0().c_str()); 3172 return ::atoi(conf->arg0().c_str());
@@ -137,22 +137,22 @@ int SrsFFMPEG::initialize_transcode(SrsConfDirective* engine) @@ -137,22 +137,22 @@ int SrsFFMPEG::initialize_transcode(SrsConfDirective* engine)
137 SRS_RTMP_ENCODER_VCODEC, vcodec.c_str(), ret); 137 SRS_RTMP_ENCODER_VCODEC, vcodec.c_str(), ret);
138 return ret; 138 return ret;
139 } 139 }
140 - if (vbitrate <= 0) { 140 + if (vbitrate < 0) {
141 ret = ERROR_ENCODER_VBITRATE; 141 ret = ERROR_ENCODER_VBITRATE;
142 srs_error("invalid vbitrate: %d, ret=%d", vbitrate, ret); 142 srs_error("invalid vbitrate: %d, ret=%d", vbitrate, ret);
143 return ret; 143 return ret;
144 } 144 }
145 - if (vfps <= 0) { 145 + if (vfps < 0) {
146 ret = ERROR_ENCODER_VFPS; 146 ret = ERROR_ENCODER_VFPS;
147 srs_error("invalid vfps: %.2f, ret=%d", vfps, ret); 147 srs_error("invalid vfps: %.2f, ret=%d", vfps, ret);
148 return ret; 148 return ret;
149 } 149 }
150 - if (vwidth <= 0) { 150 + if (vwidth < 0) {
151 ret = ERROR_ENCODER_VWIDTH; 151 ret = ERROR_ENCODER_VWIDTH;
152 srs_error("invalid vwidth: %d, ret=%d", vwidth, ret); 152 srs_error("invalid vwidth: %d, ret=%d", vwidth, ret);
153 return ret; 153 return ret;
154 } 154 }
155 - if (vheight <= 0) { 155 + if (vheight < 0) {
156 ret = ERROR_ENCODER_VHEIGHT; 156 ret = ERROR_ENCODER_VHEIGHT;
157 srs_error("invalid vheight: %d, ret=%d", vheight, ret); 157 srs_error("invalid vheight: %d, ret=%d", vheight, ret);
158 return ret; 158 return ret;
@@ -176,7 +176,7 @@ int SrsFFMPEG::initialize_transcode(SrsConfDirective* engine) @@ -176,7 +176,7 @@ int SrsFFMPEG::initialize_transcode(SrsConfDirective* engine)
176 176
177 // @see, https://github.com/simple-rtmp-server/srs/issues/145 177 // @see, https://github.com/simple-rtmp-server/srs/issues/145
178 if (acodec == SRS_RTMP_ENCODER_LIBAACPLUS && acodec != SRS_RTMP_ENCODER_LIBFDKAAC) { 178 if (acodec == SRS_RTMP_ENCODER_LIBAACPLUS && acodec != SRS_RTMP_ENCODER_LIBFDKAAC) {
179 - if (abitrate < 16 || abitrate > 72) { 179 + if (abitrate != 0 && (abitrate < 16 || abitrate > 72)) {
180 ret = ERROR_ENCODER_ABITRATE; 180 ret = ERROR_ENCODER_ABITRATE;
181 srs_error("invalid abitrate for aac: %d, must in [16, 72], ret=%d", abitrate, ret); 181 srs_error("invalid abitrate for aac: %d, must in [16, 72], ret=%d", abitrate, ret);
182 return ret; 182 return ret;
@@ -184,17 +184,17 @@ int SrsFFMPEG::initialize_transcode(SrsConfDirective* engine) @@ -184,17 +184,17 @@ int SrsFFMPEG::initialize_transcode(SrsConfDirective* engine)
184 } 184 }
185 185
186 if (acodec != SRS_RTMP_ENCODER_COPY && acodec != SRS_RTMP_ENCODER_NO_AUDIO) { 186 if (acodec != SRS_RTMP_ENCODER_COPY && acodec != SRS_RTMP_ENCODER_NO_AUDIO) {
187 - if (abitrate <= 0) { 187 + if (abitrate < 0) {
188 ret = ERROR_ENCODER_ABITRATE; 188 ret = ERROR_ENCODER_ABITRATE;
189 srs_error("invalid abitrate: %d, ret=%d", abitrate, ret); 189 srs_error("invalid abitrate: %d, ret=%d", abitrate, ret);
190 return ret; 190 return ret;
191 } 191 }
192 - if (asample_rate <= 0) { 192 + if (asample_rate < 0) {
193 ret = ERROR_ENCODER_ASAMPLE_RATE; 193 ret = ERROR_ENCODER_ASAMPLE_RATE;
194 srs_error("invalid sample rate: %d, ret=%d", asample_rate, ret); 194 srs_error("invalid sample rate: %d, ret=%d", asample_rate, ret);
195 return ret; 195 return ret;
196 } 196 }
197 - if (achannels != 1 && achannels != 2) { 197 + if (achannels != 0 && achannels != 1 && achannels != 2) {
198 ret = ERROR_ENCODER_ACHANNELS; 198 ret = ERROR_ENCODER_ACHANNELS;
199 srs_error("invalid achannels, must be 1 or 2, actual %d, ret=%d", achannels, ret); 199 srs_error("invalid achannels, must be 1 or 2, actual %d, ret=%d", achannels, ret);
200 return ret; 200 return ret;
@@ -285,26 +285,36 @@ int SrsFFMPEG::start() @@ -285,26 +285,36 @@ int SrsFFMPEG::start()
285 285
286 // the codec params is disabled when copy 286 // the codec params is disabled when copy
287 if (vcodec != SRS_RTMP_ENCODER_COPY && vcodec != SRS_RTMP_ENCODER_NO_VIDEO) { 287 if (vcodec != SRS_RTMP_ENCODER_COPY && vcodec != SRS_RTMP_ENCODER_NO_VIDEO) {
288 - params.push_back("-b:v");  
289 - snprintf(tmp, sizeof(tmp), "%d", vbitrate * 1000);  
290 - params.push_back(tmp); 288 + if (vbitrate > 0) {
  289 + params.push_back("-b:v");
  290 + snprintf(tmp, sizeof(tmp), "%d", vbitrate * 1000);
  291 + params.push_back(tmp);
  292 + }
291 293
292 - params.push_back("-r");  
293 - snprintf(tmp, sizeof(tmp), "%.2f", vfps);  
294 - params.push_back(tmp); 294 + if (vfps > 0) {
  295 + params.push_back("-r");
  296 + snprintf(tmp, sizeof(tmp), "%.2f", vfps);
  297 + params.push_back(tmp);
  298 + }
295 299
296 - params.push_back("-s");  
297 - snprintf(tmp, sizeof(tmp), "%dx%d", vwidth, vheight);  
298 - params.push_back(tmp); 300 + if (vwidth > 0 && vheight > 0) {
  301 + params.push_back("-s");
  302 + snprintf(tmp, sizeof(tmp), "%dx%d", vwidth, vheight);
  303 + params.push_back(tmp);
  304 + }
299 305
300 // TODO: add aspect if needed. 306 // TODO: add aspect if needed.
301 - params.push_back("-aspect");  
302 - snprintf(tmp, sizeof(tmp), "%d:%d", vwidth, vheight);  
303 - params.push_back(tmp); 307 + if (vwidth > 0 && vheight > 0) {
  308 + params.push_back("-aspect");
  309 + snprintf(tmp, sizeof(tmp), "%d:%d", vwidth, vheight);
  310 + params.push_back(tmp);
  311 + }
304 312
305 - params.push_back("-threads");  
306 - snprintf(tmp, sizeof(tmp), "%d", vthreads);  
307 - params.push_back(tmp); 313 + if (vthreads > 0) {
  314 + params.push_back("-threads");
  315 + snprintf(tmp, sizeof(tmp), "%d", vthreads);
  316 + params.push_back(tmp);
  317 + }
308 318
309 params.push_back("-profile:v"); 319 params.push_back("-profile:v");
310 params.push_back(vprofile); 320 params.push_back(vprofile);
@@ -335,17 +345,23 @@ int SrsFFMPEG::start() @@ -335,17 +345,23 @@ int SrsFFMPEG::start()
335 // the codec params is disabled when copy 345 // the codec params is disabled when copy
336 if (acodec != SRS_RTMP_ENCODER_NO_AUDIO) { 346 if (acodec != SRS_RTMP_ENCODER_NO_AUDIO) {
337 if (acodec != SRS_RTMP_ENCODER_COPY) { 347 if (acodec != SRS_RTMP_ENCODER_COPY) {
338 - params.push_back("-b:a");  
339 - snprintf(tmp, sizeof(tmp), "%d", abitrate * 1000);  
340 - params.push_back(tmp); 348 + if (abitrate > 0) {
  349 + params.push_back("-b:a");
  350 + snprintf(tmp, sizeof(tmp), "%d", abitrate * 1000);
  351 + params.push_back(tmp);
  352 + }
341 353
342 - params.push_back("-ar");  
343 - snprintf(tmp, sizeof(tmp), "%d", asample_rate);  
344 - params.push_back(tmp); 354 + if (asample_rate > 0) {
  355 + params.push_back("-ar");
  356 + snprintf(tmp, sizeof(tmp), "%d", asample_rate);
  357 + params.push_back(tmp);
  358 + }
345 359
346 - params.push_back("-ac");  
347 - snprintf(tmp, sizeof(tmp), "%d", achannels);  
348 - params.push_back(tmp); 360 + if (achannels > 0) {
  361 + params.push_back("-ac");
  362 + snprintf(tmp, sizeof(tmp), "%d", achannels);
  363 + params.push_back(tmp);
  364 + }
349 365
350 // aparams 366 // aparams
351 std::vector<std::string>::iterator it; 367 std::vector<std::string>::iterator it;
@@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 // current release version 31 // current release version
32 #define VERSION_MAJOR 2 32 #define VERSION_MAJOR 2
33 #define VERSION_MINOR 0 33 #define VERSION_MINOR 0
34 -#define VERSION_REVISION 179 34 +#define VERSION_REVISION 180
35 35
36 // server info. 36 // server info.
37 #define RTMP_SIG_SRS_KEY "SRS" 37 #define RTMP_SIG_SRS_KEY "SRS"