3 * Copyright (c) 2003 The Libav Project
5 * This file is part of Libav.
7 * Libav is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * Libav is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with Libav; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "libavutil/dict.h"
23 #include "libavutil/intfloat.h"
33 static const AVCodecTag flv_video_codec_ids
[] = {
34 { AV_CODEC_ID_FLV1
, FLV_CODECID_H263
},
35 { AV_CODEC_ID_FLASHSV
, FLV_CODECID_SCREEN
},
36 { AV_CODEC_ID_FLASHSV2
, FLV_CODECID_SCREEN2
},
37 { AV_CODEC_ID_VP6F
, FLV_CODECID_VP6
},
38 { AV_CODEC_ID_VP6A
, FLV_CODECID_VP6A
},
39 { AV_CODEC_ID_H264
, FLV_CODECID_H264
},
40 { AV_CODEC_ID_NONE
, 0 }
43 static const AVCodecTag flv_audio_codec_ids
[] = {
44 { AV_CODEC_ID_MP3
, FLV_CODECID_MP3
>> FLV_AUDIO_CODECID_OFFSET
},
45 { AV_CODEC_ID_PCM_U8
, FLV_CODECID_PCM
>> FLV_AUDIO_CODECID_OFFSET
},
46 { AV_CODEC_ID_PCM_S16BE
, FLV_CODECID_PCM
>> FLV_AUDIO_CODECID_OFFSET
},
47 { AV_CODEC_ID_PCM_S16LE
, FLV_CODECID_PCM_LE
>> FLV_AUDIO_CODECID_OFFSET
},
48 { AV_CODEC_ID_ADPCM_SWF
, FLV_CODECID_ADPCM
>> FLV_AUDIO_CODECID_OFFSET
},
49 { AV_CODEC_ID_AAC
, FLV_CODECID_AAC
>> FLV_AUDIO_CODECID_OFFSET
},
50 { AV_CODEC_ID_NELLYMOSER
, FLV_CODECID_NELLYMOSER
>> FLV_AUDIO_CODECID_OFFSET
},
51 { AV_CODEC_ID_PCM_MULAW
, FLV_CODECID_PCM_MULAW
>> FLV_AUDIO_CODECID_OFFSET
},
52 { AV_CODEC_ID_PCM_ALAW
, FLV_CODECID_PCM_ALAW
>> FLV_AUDIO_CODECID_OFFSET
},
53 { AV_CODEC_ID_SPEEX
, FLV_CODECID_SPEEX
>> FLV_AUDIO_CODECID_OFFSET
},
54 { AV_CODEC_ID_NONE
, 0 }
57 typedef struct FLVContext
{
59 int64_t duration_offset
;
60 int64_t filesize_offset
;
62 int64_t delay
; ///< first dts delay (needed for AVC & Speex)
64 AVCodecContext
*audio_enc
;
65 AVCodecContext
*video_enc
;
67 AVCodecContext
*data_enc
;
70 typedef struct FLVStreamContext
{
71 int64_t last_ts
; ///< last timestamp for each stream
74 static int get_audio_flags(AVFormatContext
*s
, AVCodecContext
*enc
)
76 int flags
= (enc
->bits_per_coded_sample
== 16) ? FLV_SAMPLESSIZE_16BIT
77 : FLV_SAMPLESSIZE_8BIT
;
79 if (enc
->codec_id
== AV_CODEC_ID_AAC
) // specs force these parameters
80 return FLV_CODECID_AAC
| FLV_SAMPLERATE_44100HZ
|
81 FLV_SAMPLESSIZE_16BIT
| FLV_STEREO
;
82 else if (enc
->codec_id
== AV_CODEC_ID_SPEEX
) {
83 if (enc
->sample_rate
!= 16000) {
84 av_log(s
, AV_LOG_ERROR
,
85 "flv only supports wideband (16kHz) Speex audio\n");
88 if (enc
->channels
!= 1) {
89 av_log(s
, AV_LOG_ERROR
, "flv only supports mono Speex audio\n");
92 return FLV_CODECID_SPEEX
| FLV_SAMPLERATE_11025HZ
| FLV_SAMPLESSIZE_16BIT
;
94 switch (enc
->sample_rate
) {
96 flags
|= FLV_SAMPLERATE_44100HZ
;
99 flags
|= FLV_SAMPLERATE_22050HZ
;
102 flags
|= FLV_SAMPLERATE_11025HZ
;
104 case 16000: // nellymoser only
105 case 8000: // nellymoser only
106 case 5512: // not MP3
107 if (enc
->codec_id
!= AV_CODEC_ID_MP3
) {
108 flags
|= FLV_SAMPLERATE_SPECIAL
;
112 av_log(s
, AV_LOG_ERROR
,
113 "flv does not support that sample rate, "
114 "choose from (44100, 22050, 11025).\n");
119 if (enc
->channels
> 1)
122 switch (enc
->codec_id
) {
123 case AV_CODEC_ID_MP3
:
124 flags
|= FLV_CODECID_MP3
| FLV_SAMPLESSIZE_16BIT
;
126 case AV_CODEC_ID_PCM_U8
:
127 flags
|= FLV_CODECID_PCM
| FLV_SAMPLESSIZE_8BIT
;
129 case AV_CODEC_ID_PCM_S16BE
:
130 flags
|= FLV_CODECID_PCM
| FLV_SAMPLESSIZE_16BIT
;
132 case AV_CODEC_ID_PCM_S16LE
:
133 flags
|= FLV_CODECID_PCM_LE
| FLV_SAMPLESSIZE_16BIT
;
135 case AV_CODEC_ID_ADPCM_SWF
:
136 flags
|= FLV_CODECID_ADPCM
| FLV_SAMPLESSIZE_16BIT
;
138 case AV_CODEC_ID_NELLYMOSER
:
139 if (enc
->sample_rate
== 8000)
140 flags
|= FLV_CODECID_NELLYMOSER_8KHZ_MONO
| FLV_SAMPLESSIZE_16BIT
;
141 else if (enc
->sample_rate
== 16000)
142 flags
|= FLV_CODECID_NELLYMOSER_16KHZ_MONO
| FLV_SAMPLESSIZE_16BIT
;
144 flags
|= FLV_CODECID_NELLYMOSER
| FLV_SAMPLESSIZE_16BIT
;
146 case AV_CODEC_ID_PCM_MULAW
:
147 flags
= FLV_CODECID_PCM_MULAW
| FLV_SAMPLERATE_SPECIAL
| FLV_SAMPLESSIZE_16BIT
;
149 case AV_CODEC_ID_PCM_ALAW
:
150 flags
= FLV_CODECID_PCM_ALAW
| FLV_SAMPLERATE_SPECIAL
| FLV_SAMPLESSIZE_16BIT
;
153 flags
|= enc
->codec_tag
<< 4;
156 av_log(s
, AV_LOG_ERROR
, "codec not compatible with flv\n");
163 static void put_amf_string(AVIOContext
*pb
, const char *str
)
165 size_t len
= strlen(str
);
167 avio_write(pb
, str
, len
);
170 static void put_avc_eos_tag(AVIOContext
*pb
, unsigned ts
)
172 avio_w8(pb
, FLV_TAG_TYPE_VIDEO
);
173 avio_wb24(pb
, 5); /* Tag Data Size */
174 avio_wb24(pb
, ts
); /* lower 24 bits of timestamp in ms */
175 avio_w8(pb
, (ts
>> 24) & 0x7F); /* MSB of ts in ms */
176 avio_wb24(pb
, 0); /* StreamId = 0 */
177 avio_w8(pb
, 23); /* ub[4] FrameType = 1, ub[4] CodecId = 7 */
178 avio_w8(pb
, 2); /* AVC end of sequence */
179 avio_wb24(pb
, 0); /* Always 0 for AVC EOS. */
180 avio_wb32(pb
, 16); /* Size of FLV tag */
183 static void put_amf_double(AVIOContext
*pb
, double d
)
185 avio_w8(pb
, AMF_DATA_TYPE_NUMBER
);
186 avio_wb64(pb
, av_double2int(d
));
189 static void put_amf_bool(AVIOContext
*pb
, int b
)
191 avio_w8(pb
, AMF_DATA_TYPE_BOOL
);
195 static void write_metadata(AVFormatContext
*s
, unsigned int ts
)
197 AVIOContext
*pb
= s
->pb
;
198 FLVContext
*flv
= s
->priv_data
;
199 int metadata_count
= 0;
200 int64_t metadata_size_pos
, data_size
, metadata_count_pos
;
201 AVDictionaryEntry
*tag
= NULL
;
204 avio_w8(pb
, 18); // tag type META
205 metadata_size_pos
= avio_tell(pb
);
206 avio_wb24(pb
, 0); // size of data part (sum of all parts below)
207 avio_wb24(pb
, ts
); // timestamp
208 avio_wb32(pb
, 0); // reserved
210 /* now data of data_size size */
212 /* first event name as a string */
213 avio_w8(pb
, AMF_DATA_TYPE_STRING
);
214 put_amf_string(pb
, "onMetaData"); // 12 bytes
216 /* mixed array (hash) with size and string/type/data tuples */
217 avio_w8(pb
, AMF_DATA_TYPE_MIXEDARRAY
);
218 metadata_count_pos
= avio_tell(pb
);
219 metadata_count
= 4 * !!flv
->video_enc
+
220 5 * !!flv
->audio_enc
+
221 1 * !!flv
->data_enc
+
222 2; // +2 for duration and file size
224 avio_wb32(pb
, metadata_count
);
226 put_amf_string(pb
, "duration");
227 flv
->duration_offset
= avio_tell(pb
);
229 // fill in the guessed duration, it'll be corrected later if incorrect
230 put_amf_double(pb
, s
->duration
/ AV_TIME_BASE
);
232 if (flv
->video_enc
) {
233 put_amf_string(pb
, "width");
234 put_amf_double(pb
, flv
->video_enc
->width
);
236 put_amf_string(pb
, "height");
237 put_amf_double(pb
, flv
->video_enc
->height
);
239 put_amf_string(pb
, "videodatarate");
240 put_amf_double(pb
, flv
->video_enc
->bit_rate
/ 1024.0);
242 if (flv
->framerate
!= 0.0) {
243 put_amf_string(pb
, "framerate");
244 put_amf_double(pb
, flv
->framerate
);
248 put_amf_string(pb
, "videocodecid");
249 put_amf_double(pb
, flv
->video_enc
->codec_tag
);
252 if (flv
->audio_enc
) {
253 put_amf_string(pb
, "audiodatarate");
254 put_amf_double(pb
, flv
->audio_enc
->bit_rate
/ 1024.0);
256 put_amf_string(pb
, "audiosamplerate");
257 put_amf_double(pb
, flv
->audio_enc
->sample_rate
);
259 put_amf_string(pb
, "audiosamplesize");
260 put_amf_double(pb
, flv
->audio_enc
->codec_id
== AV_CODEC_ID_PCM_U8 ?
8 : 16);
262 put_amf_string(pb
, "stereo");
263 put_amf_bool(pb
, flv
->audio_enc
->channels
== 2);
265 put_amf_string(pb
, "audiocodecid");
266 put_amf_double(pb
, flv
->audio_enc
->codec_tag
);
270 put_amf_string(pb
, "datastream");
271 put_amf_double(pb
, 0.0);
274 while ((tag
= av_dict_get(s
->metadata
, "", tag
, AV_DICT_IGNORE_SUFFIX
))) {
275 put_amf_string(pb
, tag
->key
);
276 avio_w8(pb
, AMF_DATA_TYPE_STRING
);
277 put_amf_string(pb
, tag
->value
);
281 put_amf_string(pb
, "filesize");
282 flv
->filesize_offset
= avio_tell(pb
);
283 put_amf_double(pb
, 0); // delayed write
285 put_amf_string(pb
, "");
286 avio_w8(pb
, AMF_END_OF_OBJECT
);
288 /* write total size of tag */
289 data_size
= avio_tell(pb
) - metadata_size_pos
- 10;
291 avio_seek(pb
, metadata_count_pos
, SEEK_SET
);
292 avio_wb32(pb
, metadata_count
);
294 avio_seek(pb
, metadata_size_pos
, SEEK_SET
);
295 avio_wb24(pb
, data_size
);
296 avio_skip(pb
, data_size
+ 10 - 3);
297 avio_wb32(pb
, data_size
+ 11);
300 static int flv_write_header(AVFormatContext
*s
)
303 AVIOContext
*pb
= s
->pb
;
304 FLVContext
*flv
= s
->priv_data
;
307 for (i
= 0; i
< s
->nb_streams
; i
++) {
308 AVCodecContext
*enc
= s
->streams
[i
]->codec
;
309 FLVStreamContext
*sc
;
310 switch (enc
->codec_type
) {
311 case AVMEDIA_TYPE_VIDEO
:
312 if (s
->streams
[i
]->avg_frame_rate
.den
&&
313 s
->streams
[i
]->avg_frame_rate
.num
) {
314 flv
->framerate
= av_q2d(s
->streams
[i
]->avg_frame_rate
);
316 if (flv
->video_enc
) {
317 av_log(s
, AV_LOG_ERROR
,
318 "at most one video stream is supported in flv\n");
319 return AVERROR(EINVAL
);
321 flv
->video_enc
= enc
;
322 if (enc
->codec_tag
== 0) {
323 av_log(s
, AV_LOG_ERROR
, "video codec not compatible with flv\n");
327 case AVMEDIA_TYPE_AUDIO
:
328 if (flv
->audio_enc
) {
329 av_log(s
, AV_LOG_ERROR
,
330 "at most one audio stream is supported in flv\n");
331 return AVERROR(EINVAL
);
333 flv
->audio_enc
= enc
;
334 if (get_audio_flags(s
, enc
) < 0)
335 return AVERROR_INVALIDDATA
;
337 case AVMEDIA_TYPE_DATA
:
338 if (enc
->codec_id
!= AV_CODEC_ID_TEXT
) {
339 av_log(s
, AV_LOG_ERROR
, "codec not compatible with flv\n");
340 return AVERROR_INVALIDDATA
;
345 av_log(s
, AV_LOG_ERROR
, "codec not compatible with flv\n");
348 avpriv_set_pts_info(s
->streams
[i
], 32, 1, 1000); /* 32 bit pts in ms */
350 sc
= av_mallocz(sizeof(FLVStreamContext
));
352 return AVERROR(ENOMEM
);
353 s
->streams
[i
]->priv_data
= sc
;
357 flv
->delay
= AV_NOPTS_VALUE
;
359 avio_write(pb
, "FLV", 3);
361 avio_w8(pb
, FLV_HEADER_FLAG_HASAUDIO
* !!flv
->audio_enc
+
362 FLV_HEADER_FLAG_HASVIDEO
* !!flv
->video_enc
);
366 for (i
= 0; i
< s
->nb_streams
; i
++)
367 if (s
->streams
[i
]->codec
->codec_tag
== 5) {
368 avio_w8(pb
, 8); // message type
369 avio_wb24(pb
, 0); // include flags
370 avio_wb24(pb
, 0); // time stamp
371 avio_wb32(pb
, 0); // reserved
372 avio_wb32(pb
, 11); // size
376 write_metadata(s
, 0);
378 for (i
= 0; i
< s
->nb_streams
; i
++) {
379 AVCodecContext
*enc
= s
->streams
[i
]->codec
;
380 if (enc
->codec_id
== AV_CODEC_ID_AAC
|| enc
->codec_id
== AV_CODEC_ID_H264
) {
382 avio_w8(pb
, enc
->codec_type
== AVMEDIA_TYPE_VIDEO ?
383 FLV_TAG_TYPE_VIDEO
: FLV_TAG_TYPE_AUDIO
);
384 avio_wb24(pb
, 0); // size patched later
385 avio_wb24(pb
, 0); // ts
386 avio_w8(pb
, 0); // ts ext
387 avio_wb24(pb
, 0); // streamid
389 if (enc
->codec_id
== AV_CODEC_ID_AAC
) {
390 avio_w8(pb
, get_audio_flags(s
, enc
));
391 avio_w8(pb
, 0); // AAC sequence header
392 avio_write(pb
, enc
->extradata
, enc
->extradata_size
);
394 avio_w8(pb
, enc
->codec_tag
| FLV_FRAME_KEY
); // flags
395 avio_w8(pb
, 0); // AVC sequence header
396 avio_wb24(pb
, 0); // composition time
397 ff_isom_write_avcc(pb
, enc
->extradata
, enc
->extradata_size
);
399 data_size
= avio_tell(pb
) - pos
;
400 avio_seek(pb
, -data_size
- 10, SEEK_CUR
);
401 avio_wb24(pb
, data_size
);
402 avio_skip(pb
, data_size
+ 10 - 3);
403 avio_wb32(pb
, data_size
+ 11); // previous tag size
410 static int flv_write_trailer(AVFormatContext
*s
)
414 AVIOContext
*pb
= s
->pb
;
415 FLVContext
*flv
= s
->priv_data
;
419 for (i
= 0; i
< s
->nb_streams
; i
++) {
420 AVCodecContext
*enc
= s
->streams
[i
]->codec
;
421 FLVStreamContext
*sc
= s
->streams
[i
]->priv_data
;
422 if (enc
->codec_type
== AVMEDIA_TYPE_VIDEO
&&
423 enc
->codec_id
== AV_CODEC_ID_H264
)
424 put_avc_eos_tag(pb
, sc
->last_ts
);
427 file_size
= avio_tell(pb
);
429 /* update information */
430 if (avio_seek(pb
, flv
->duration_offset
, SEEK_SET
) < 0)
431 av_log(s
, AV_LOG_WARNING
, "Failed to update header with correct duration.\n");
433 put_amf_double(pb
, flv
->duration
/ (double)1000);
434 if (avio_seek(pb
, flv
->filesize_offset
, SEEK_SET
) < 0)
435 av_log(s
, AV_LOG_WARNING
, "Failed to update header with correct filesize.\n");
437 put_amf_double(pb
, file_size
);
439 avio_seek(pb
, file_size
, SEEK_SET
);
443 static int flv_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
445 AVIOContext
*pb
= s
->pb
;
446 AVCodecContext
*enc
= s
->streams
[pkt
->stream_index
]->codec
;
447 FLVContext
*flv
= s
->priv_data
;
448 FLVStreamContext
*sc
= s
->streams
[pkt
->stream_index
]->priv_data
;
450 int size
= pkt
->size
;
451 uint8_t *data
= NULL
;
452 int flags
= 0, flags_size
;
454 if (enc
->codec_id
== AV_CODEC_ID_VP6F
|| enc
->codec_id
== AV_CODEC_ID_VP6A
||
455 enc
->codec_id
== AV_CODEC_ID_AAC
)
457 else if (enc
->codec_id
== AV_CODEC_ID_H264
)
462 if (flv
->delay
== AV_NOPTS_VALUE
)
463 flv
->delay
= -pkt
->dts
;
465 if (pkt
->dts
< -flv
->delay
) {
466 av_log(s
, AV_LOG_WARNING
,
467 "Packets are not in the proper order with respect to DTS\n");
468 return AVERROR(EINVAL
);
471 ts
= pkt
->dts
+ flv
->delay
; // add delay to force positive dts
473 if (s
->event_flags
& AVSTREAM_EVENT_FLAG_METADATA_UPDATED
) {
474 write_metadata(s
, ts
);
475 s
->event_flags
&= ~AVSTREAM_EVENT_FLAG_METADATA_UPDATED
;
478 switch (enc
->codec_type
) {
479 case AVMEDIA_TYPE_VIDEO
:
480 avio_w8(pb
, FLV_TAG_TYPE_VIDEO
);
482 flags
= enc
->codec_tag
;
484 av_log(s
, AV_LOG_ERROR
,
485 "video codec %X not compatible with flv\n",
490 flags
|= pkt
->flags
& AV_PKT_FLAG_KEY ? FLV_FRAME_KEY
: FLV_FRAME_INTER
;
492 case AVMEDIA_TYPE_AUDIO
:
493 flags
= get_audio_flags(s
, enc
);
497 avio_w8(pb
, FLV_TAG_TYPE_AUDIO
);
499 case AVMEDIA_TYPE_DATA
:
500 avio_w8(pb
, FLV_TAG_TYPE_META
);
503 return AVERROR(EINVAL
);
506 if (enc
->codec_id
== AV_CODEC_ID_H264
)
507 /* check if extradata looks like MP4 */
508 if (enc
->extradata_size
> 0 && *(uint8_t*)enc
->extradata
!= 1)
509 if (ff_avc_parse_nal_units_buf(pkt
->data
, &data
, &size
) < 0)
512 /* check Speex packet duration */
513 if (enc
->codec_id
== AV_CODEC_ID_SPEEX
&& ts
- sc
->last_ts
> 160)
514 av_log(s
, AV_LOG_WARNING
, "Warning: Speex stream has more than "
515 "8 frames per packet. Adobe Flash "
516 "Player cannot handle this!\n");
518 if (sc
->last_ts
< ts
)
521 avio_wb24(pb
, size
+ flags_size
);
523 avio_w8(pb
, (ts
>> 24) & 0x7F); // timestamps are 32 bits _signed_
524 avio_wb24(pb
, flv
->reserved
);
526 if (enc
->codec_type
== AVMEDIA_TYPE_DATA
) {
528 int64_t metadata_size_pos
= avio_tell(pb
);
529 avio_w8(pb
, AMF_DATA_TYPE_STRING
);
530 put_amf_string(pb
, "onTextData");
531 avio_w8(pb
, AMF_DATA_TYPE_MIXEDARRAY
);
533 put_amf_string(pb
, "type");
534 avio_w8(pb
, AMF_DATA_TYPE_STRING
);
535 put_amf_string(pb
, "Text");
536 put_amf_string(pb
, "text");
537 avio_w8(pb
, AMF_DATA_TYPE_STRING
);
538 put_amf_string(pb
, pkt
->data
);
539 put_amf_string(pb
, "");
540 avio_w8(pb
, AMF_END_OF_OBJECT
);
541 /* write total size of tag */
542 data_size
= avio_tell(pb
) - metadata_size_pos
;
543 avio_seek(pb
, metadata_size_pos
- 10, SEEK_SET
);
544 avio_wb24(pb
, data_size
);
545 avio_seek(pb
, data_size
+ 10 - 3, SEEK_CUR
);
546 avio_wb32(pb
, data_size
+ 11);
549 if (enc
->codec_id
== AV_CODEC_ID_VP6F
|| enc
->codec_id
== AV_CODEC_ID_VP6A
) {
550 if (enc
->extradata_size
)
551 avio_w8(pb
, enc
->extradata
[0]);
553 avio_w8(pb
, ((FFALIGN(enc
->width
, 16) - enc
->width
) << 4) |
554 (FFALIGN(enc
->height
, 16) - enc
->height
));
555 } else if (enc
->codec_id
== AV_CODEC_ID_AAC
)
556 avio_w8(pb
, 1); // AAC raw
557 else if (enc
->codec_id
== AV_CODEC_ID_H264
) {
558 avio_w8(pb
, 1); // AVC NALU
559 avio_wb24(pb
, pkt
->pts
- pkt
->dts
);
562 avio_write(pb
, data ? data
: pkt
->data
, size
);
564 avio_wb32(pb
, size
+ flags_size
+ 11); // previous tag size
565 flv
->duration
= FFMAX(flv
->duration
,
566 pkt
->pts
+ flv
->delay
+ pkt
->duration
);
574 AVOutputFormat ff_flv_muxer
= {
576 .long_name
= NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),
577 .mime_type
= "video/x-flv",
579 .priv_data_size
= sizeof(FLVContext
),
580 .audio_codec
= CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3
: AV_CODEC_ID_ADPCM_SWF
,
581 .video_codec
= AV_CODEC_ID_FLV1
,
582 .write_header
= flv_write_header
,
583 .write_packet
= flv_write_packet
,
584 .write_trailer
= flv_write_trailer
,
585 .codec_tag
= (const AVCodecTag
* const []) {
586 flv_video_codec_ids
, flv_audio_codec_ids
, 0
588 .flags
= AVFMT_GLOBALHEADER
| AVFMT_VARIABLE_FPS
|