lavf/sdp: add more thorough error handling

Return error codes when constructing a stream config fails, rather than
just disregarding the failure and continuing.
Propagate the error codes from av_sdp_create().
pull/379/head
Anton Khirnov 3 years ago
parent b0518f9977
commit fe31708eaa
  1. 3
      libavformat/internal.h
  2. 172
      libavformat/sdp.c

@ -556,8 +556,9 @@ uint64_t ff_parse_ntp_time(uint64_t ntp_ts);
* @param ttl the time to live of the stream, 0 if not multicast * @param ttl the time to live of the stream, 0 if not multicast
* @param fmt the AVFormatContext, which might contain options modifying * @param fmt the AVFormatContext, which might contain options modifying
* the generated SDP * the generated SDP
* @return 0 on success, a negative error code on failure
*/ */
void ff_sdp_write_media(char *buff, int size, const AVStream *st, int idx, int ff_sdp_write_media(char *buff, int size, const AVStream *st, int idx,
const char *dest_addr, const char *dest_type, const char *dest_addr, const char *dest_type,
int port, int ttl, AVFormatContext *fmt); int port, int ttl, AVFormatContext *fmt);

@ -151,7 +151,8 @@ static int sdp_get_address(char *dest_addr, int size, int *ttl, const char *url)
} }
#define MAX_PSET_SIZE 1024 #define MAX_PSET_SIZE 1024
static char *extradata2psets(AVFormatContext *s, const AVCodecParameters *par) static int extradata2psets(AVFormatContext *s, const AVCodecParameters *par,
char **out)
{ {
char *psets, *p; char *psets, *p;
const uint8_t *r; const uint8_t *r;
@ -162,15 +163,18 @@ static char *extradata2psets(AVFormatContext *s, const AVCodecParameters *par)
uint8_t *tmpbuf = NULL; uint8_t *tmpbuf = NULL;
const uint8_t *sps = NULL, *sps_end; const uint8_t *sps = NULL, *sps_end;
*out = NULL;
if (par->extradata_size > MAX_EXTRADATA_SIZE) { if (par->extradata_size > MAX_EXTRADATA_SIZE) {
av_log(s, AV_LOG_ERROR, "Too much extradata!\n"); av_log(s, AV_LOG_ERROR, "Too much extradata!\n");
return AVERROR_INVALIDDATA;
return NULL;
} }
if (par->extradata[0] == 1) { if (par->extradata[0] == 1) {
if (ff_avc_write_annexb_extradata(par->extradata, &extradata, int ret = ff_avc_write_annexb_extradata(par->extradata, &extradata,
&extradata_size)) &extradata_size);
return NULL; if (ret < 0)
return ret;
tmpbuf = extradata; tmpbuf = extradata;
} }
@ -178,7 +182,7 @@ static char *extradata2psets(AVFormatContext *s, const AVCodecParameters *par)
if (!psets) { if (!psets) {
av_log(s, AV_LOG_ERROR, "Cannot allocate memory for the parameter sets.\n"); av_log(s, AV_LOG_ERROR, "Cannot allocate memory for the parameter sets.\n");
av_free(tmpbuf); av_free(tmpbuf);
return NULL; return AVERROR(ENOMEM);
} }
memcpy(psets, pset_string, strlen(pset_string)); memcpy(psets, pset_string, strlen(pset_string));
p = psets + strlen(pset_string); p = psets + strlen(pset_string);
@ -203,11 +207,12 @@ static char *extradata2psets(AVFormatContext *s, const AVCodecParameters *par)
sps_end = r1; sps_end = r1;
} }
if (!av_base64_encode(p, MAX_PSET_SIZE - (p - psets), r, r1 - r)) { if (!av_base64_encode(p, MAX_PSET_SIZE - (p - psets), r, r1 - r)) {
av_log(s, AV_LOG_ERROR, "Cannot Base64-encode %"PTRDIFF_SPECIFIER" %"PTRDIFF_SPECIFIER"!\n", MAX_PSET_SIZE - (p - psets), r1 - r); av_log(s, AV_LOG_ERROR, "Cannot Base64-encode %"PTRDIFF_SPECIFIER" %"PTRDIFF_SPECIFIER"!\n",
MAX_PSET_SIZE - (p - psets), r1 - r);
av_free(psets); av_free(psets);
av_free(tmpbuf); av_free(tmpbuf);
return NULL; return AVERROR_INVALIDDATA;
} }
p += strlen(p); p += strlen(p);
r = r1; r = r1;
@ -219,10 +224,11 @@ static char *extradata2psets(AVFormatContext *s, const AVCodecParameters *par)
} }
av_free(tmpbuf); av_free(tmpbuf);
return psets; *out = psets;
return 0;
} }
static char *extradata2psets_hevc(const AVCodecParameters *par) static int extradata2psets_hevc(const AVCodecParameters *par, char **out)
{ {
char *psets; char *psets;
uint8_t *extradata = par->extradata; uint8_t *extradata = par->extradata;
@ -231,7 +237,9 @@ static char *extradata2psets_hevc(const AVCodecParameters *par)
int ps_pos[3] = { 0 }; int ps_pos[3] = { 0 };
static const char * const ps_names[3] = { "vps", "sps", "pps" }; static const char * const ps_names[3] = { "vps", "sps", "pps" };
int num_arrays, num_nalus; int num_arrays, num_nalus;
int pos, i, j; int pos, i, j, ret;
*out = NULL;
// Convert to hvcc format. Since we need to group multiple NALUs of // Convert to hvcc format. Since we need to group multiple NALUs of
// the same type, and we might need to convert from one format to the // the same type, and we might need to convert from one format to the
@ -239,9 +247,13 @@ static char *extradata2psets_hevc(const AVCodecParameters *par)
// format. // format.
if (par->extradata[0] != 1) { if (par->extradata[0] != 1) {
AVIOContext *pb; AVIOContext *pb;
if (avio_open_dyn_buf(&pb) < 0)
return NULL; ret = avio_open_dyn_buf(&pb);
if (ff_isom_write_hvcc(pb, par->extradata, par->extradata_size, 0) < 0) { if (ret < 0)
return ret;
ret = ff_isom_write_hvcc(pb, par->extradata, par->extradata_size, 0);
if (ret < 0) {
avio_close_dyn_buf(pb, &tmpbuf); avio_close_dyn_buf(pb, &tmpbuf);
goto err; goto err;
} }
@ -284,8 +296,11 @@ static char *extradata2psets_hevc(const AVCodecParameters *par)
goto err; goto err;
psets = av_mallocz(MAX_PSET_SIZE); psets = av_mallocz(MAX_PSET_SIZE);
if (!psets) if (!psets) {
ret = AVERROR(ENOMEM);
goto err; goto err;
}
psets[0] = '\0'; psets[0] = '\0';
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
@ -316,40 +331,48 @@ static char *extradata2psets_hevc(const AVCodecParameters *par)
} }
av_free(tmpbuf); av_free(tmpbuf);
return psets; *out = psets;
return 0;
err: err:
if (ret >= 0)
ret = AVERROR_INVALIDDATA;
av_free(tmpbuf); av_free(tmpbuf);
return NULL; return ret;
} }
static char *extradata2config(AVFormatContext *s, const AVCodecParameters *par) static int extradata2config(AVFormatContext *s, const AVCodecParameters *par,
char **out)
{ {
char *config; char *config;
*out = NULL;
if (par->extradata_size > MAX_EXTRADATA_SIZE) { if (par->extradata_size > MAX_EXTRADATA_SIZE) {
av_log(s, AV_LOG_ERROR, "Too much extradata!\n"); av_log(s, AV_LOG_ERROR, "Too much extradata!\n");
return AVERROR_INVALIDDATA;
return NULL;
} }
config = av_malloc(10 + par->extradata_size * 2); config = av_malloc(10 + par->extradata_size * 2);
if (!config) { if (!config) {
av_log(s, AV_LOG_ERROR, "Cannot allocate memory for the config info.\n"); av_log(s, AV_LOG_ERROR, "Cannot allocate memory for the config info.\n");
return NULL; return AVERROR(ENOMEM);
} }
memcpy(config, "; config=", 9); memcpy(config, "; config=", 9);
ff_data_to_hex(config + 9, par->extradata, par->extradata_size, 0); ff_data_to_hex(config + 9, par->extradata, par->extradata_size, 0);
return config; *out = config;
return 0;
} }
static char *xiph_extradata2config(AVFormatContext *s, const AVCodecParameters *par) static int xiph_extradata2config(AVFormatContext *s, const AVCodecParameters *par,
char **out)
{ {
uint8_t *config; uint8_t *config;
char *encoded_config; char *encoded_config;
const uint8_t *header_start[3]; const uint8_t *header_start[3];
int headers_len, header_len[3], config_len; int headers_len, header_len[3], config_len;
int first_header_size; int first_header_size, ret;
*out = NULL;
switch (par->codec_id) { switch (par->codec_id) {
case AV_CODEC_ID_THEORA: case AV_CODEC_ID_THEORA:
@ -360,14 +383,15 @@ static char *xiph_extradata2config(AVFormatContext *s, const AVCodecParameters *
break; break;
default: default:
av_log(s, AV_LOG_ERROR, "Unsupported Xiph codec ID\n"); av_log(s, AV_LOG_ERROR, "Unsupported Xiph codec ID\n");
return NULL; return AVERROR(ENOSYS);
} }
if (avpriv_split_xiph_headers(par->extradata, par->extradata_size, ret = avpriv_split_xiph_headers(par->extradata, par->extradata_size,
first_header_size, header_start, first_header_size, header_start,
header_len) < 0) { header_len);
if (ret < 0) {
av_log(s, AV_LOG_ERROR, "Extradata corrupt.\n"); av_log(s, AV_LOG_ERROR, "Extradata corrupt.\n");
return NULL; return ret;
} }
headers_len = header_len[0] + header_len[2]; headers_len = header_len[0] + header_len[2];
@ -405,12 +429,13 @@ static char *xiph_extradata2config(AVFormatContext *s, const AVCodecParameters *
config, config_len); config, config_len);
av_free(config); av_free(config);
return encoded_config; *out = encoded_config;
return 0;
xiph_fail: xiph_fail:
av_log(s, AV_LOG_ERROR, av_log(s, AV_LOG_ERROR,
"Not enough memory for configuration string\n"); "Not enough memory for configuration string\n");
return NULL; return AVERROR(ENOMEM);
} }
static int latm_context2profilelevel(const AVCodecParameters *par) static int latm_context2profilelevel(const AVCodecParameters *par)
@ -442,7 +467,8 @@ static int latm_context2profilelevel(const AVCodecParameters *par)
return profile_level; return profile_level;
} }
static char *latm_context2config(AVFormatContext *s, const AVCodecParameters *par) static int latm_context2config(AVFormatContext *s, const AVCodecParameters *par,
char **out)
{ {
/* MP4A-LATM /* MP4A-LATM
* The RTP payload format specification is described in RFC 3016 * The RTP payload format specification is described in RFC 3016
@ -452,12 +478,14 @@ static char *latm_context2config(AVFormatContext *s, const AVCodecParameters *pa
int rate_index; int rate_index;
char *config; char *config;
*out = NULL;
for (rate_index = 0; rate_index < 16; rate_index++) for (rate_index = 0; rate_index < 16; rate_index++)
if (avpriv_mpeg4audio_sample_rates[rate_index] == par->sample_rate) if (avpriv_mpeg4audio_sample_rates[rate_index] == par->sample_rate)
break; break;
if (rate_index == 16) { if (rate_index == 16) {
av_log(s, AV_LOG_ERROR, "Unsupported sample rate\n"); av_log(s, AV_LOG_ERROR, "Unsupported sample rate\n");
return NULL; return AVERROR(ENOSYS);
} }
config_byte[0] = 0x40; config_byte[0] = 0x40;
@ -470,18 +498,20 @@ static char *latm_context2config(AVFormatContext *s, const AVCodecParameters *pa
config = av_malloc(6*2+1); config = av_malloc(6*2+1);
if (!config) { if (!config) {
av_log(s, AV_LOG_ERROR, "Cannot allocate memory for the config info.\n"); av_log(s, AV_LOG_ERROR, "Cannot allocate memory for the config info.\n");
return NULL; return AVERROR(ENOMEM);
} }
ff_data_to_hex(config, config_byte, 6, 1); ff_data_to_hex(config, config_byte, 6, 1);
return config; *out = config;
return 0;
} }
static char *sdp_write_media_attributes(char *buff, int size, const AVStream *st, static int sdp_write_media_attributes(char *buff, int size, const AVStream *st,
int payload_type, AVFormatContext *fmt) int payload_type, AVFormatContext *fmt)
{ {
char *config = NULL; char *config = NULL;
const AVCodecParameters *p = st->codecpar; const AVCodecParameters *p = st->codecpar;
int ret = 0;
switch (p->codec_id) { switch (p->codec_id) {
case AV_CODEC_ID_DIRAC: case AV_CODEC_ID_DIRAC:
@ -493,7 +523,9 @@ static char *sdp_write_media_attributes(char *buff, int size, const AVStream *st
av_opt_flag_is_set(fmt->priv_data, "rtpflags", "h264_mode0")) av_opt_flag_is_set(fmt->priv_data, "rtpflags", "h264_mode0"))
mode = 0; mode = 0;
if (p->extradata_size) { if (p->extradata_size) {
config = extradata2psets(fmt, p); ret = extradata2psets(fmt, p, &config);
if (ret < 0)
return ret;
} }
av_strlcatf(buff, size, "a=rtpmap:%d H264/90000\r\n" av_strlcatf(buff, size, "a=rtpmap:%d H264/90000\r\n"
"a=fmtp:%d packetization-mode=%d%s\r\n", "a=fmtp:%d packetization-mode=%d%s\r\n",
@ -530,8 +562,11 @@ static char *sdp_write_media_attributes(char *buff, int size, const AVStream *st
payload_type, p->width, p->height); payload_type, p->width, p->height);
break; break;
case AV_CODEC_ID_HEVC: case AV_CODEC_ID_HEVC:
if (p->extradata_size) if (p->extradata_size) {
config = extradata2psets_hevc(p); ret = extradata2psets_hevc(p, &config);
if (ret < 0)
return ret;
}
av_strlcatf(buff, size, "a=rtpmap:%d H265/90000\r\n", payload_type); av_strlcatf(buff, size, "a=rtpmap:%d H265/90000\r\n", payload_type);
if (config) if (config)
av_strlcatf(buff, size, "a=fmtp:%d %s\r\n", av_strlcatf(buff, size, "a=fmtp:%d %s\r\n",
@ -539,7 +574,9 @@ static char *sdp_write_media_attributes(char *buff, int size, const AVStream *st
break; break;
case AV_CODEC_ID_MPEG4: case AV_CODEC_ID_MPEG4:
if (p->extradata_size) { if (p->extradata_size) {
config = extradata2config(fmt, p); ret = extradata2config(fmt, p, &config);
if (ret < 0)
return ret;
} }
av_strlcatf(buff, size, "a=rtpmap:%d MP4V-ES/90000\r\n" av_strlcatf(buff, size, "a=rtpmap:%d MP4V-ES/90000\r\n"
"a=fmtp:%d profile-level-id=1%s\r\n", "a=fmtp:%d profile-level-id=1%s\r\n",
@ -549,25 +586,24 @@ static char *sdp_write_media_attributes(char *buff, int size, const AVStream *st
case AV_CODEC_ID_AAC: case AV_CODEC_ID_AAC:
if (fmt && fmt->oformat && fmt->oformat->priv_class && if (fmt && fmt->oformat && fmt->oformat->priv_class &&
av_opt_flag_is_set(fmt->priv_data, "rtpflags", "latm")) { av_opt_flag_is_set(fmt->priv_data, "rtpflags", "latm")) {
config = latm_context2config(fmt, p); ret = latm_context2config(fmt, p, &config);
if (!config) if (ret < 0)
return NULL; return ret;
av_strlcatf(buff, size, "a=rtpmap:%d MP4A-LATM/%d/%d\r\n" av_strlcatf(buff, size, "a=rtpmap:%d MP4A-LATM/%d/%d\r\n"
"a=fmtp:%d profile-level-id=%d;cpresent=0;config=%s\r\n", "a=fmtp:%d profile-level-id=%d;cpresent=0;config=%s\r\n",
payload_type, p->sample_rate, p->channels, payload_type, p->sample_rate, p->channels,
payload_type, latm_context2profilelevel(p), config); payload_type, latm_context2profilelevel(p), config);
} else { } else {
if (p->extradata_size) { if (p->extradata_size) {
config = extradata2config(fmt, p); ret = extradata2config(fmt, p, &config);
if (ret < 0)
return ret;
} else { } else {
/* FIXME: maybe we can forge config information based on the /* FIXME: maybe we can forge config information based on the
* codec parameters... * codec parameters...
*/ */
av_log(fmt, AV_LOG_ERROR, "AAC with no global headers is currently not supported.\n"); av_log(fmt, AV_LOG_ERROR, "AAC with no global headers is currently not supported.\n");
return NULL; return AVERROR(ENOSYS);
}
if (!config) {
return NULL;
} }
av_strlcatf(buff, size, "a=rtpmap:%d MPEG4-GENERIC/%d/%d\r\n" av_strlcatf(buff, size, "a=rtpmap:%d MPEG4-GENERIC/%d/%d\r\n"
"a=fmtp:%d profile-level-id=1;" "a=fmtp:%d profile-level-id=1;"
@ -615,11 +651,13 @@ static char *sdp_write_media_attributes(char *buff, int size, const AVStream *st
break; break;
case AV_CODEC_ID_VORBIS: case AV_CODEC_ID_VORBIS:
if (p->extradata_size) if (p->extradata_size)
config = xiph_extradata2config(fmt, p); ret = xiph_extradata2config(fmt, p, &config);
else else {
av_log(fmt, AV_LOG_ERROR, "Vorbis configuration info missing\n"); av_log(fmt, AV_LOG_ERROR, "Vorbis configuration info missing\n");
if (!config) ret = AVERROR_INVALIDDATA;
return NULL; }
if (ret < 0)
return ret;
av_strlcatf(buff, size, "a=rtpmap:%d vorbis/%d/%d\r\n" av_strlcatf(buff, size, "a=rtpmap:%d vorbis/%d/%d\r\n"
"a=fmtp:%d configuration=%s\r\n", "a=fmtp:%d configuration=%s\r\n",
@ -640,15 +678,17 @@ static char *sdp_write_media_attributes(char *buff, int size, const AVStream *st
break; break;
default: default:
av_log(fmt, AV_LOG_ERROR, "Unsupported pixel format.\n"); av_log(fmt, AV_LOG_ERROR, "Unsupported pixel format.\n");
return NULL; return AVERROR(ENOSYS);
} }
if (p->extradata_size) if (p->extradata_size)
config = xiph_extradata2config(fmt, p); ret = xiph_extradata2config(fmt, p, &config);
else else {
av_log(fmt, AV_LOG_ERROR, "Theora configuration info missing\n"); av_log(fmt, AV_LOG_ERROR, "Theora configuration info missing\n");
if (!config) ret = AVERROR_INVALIDDATA;
return NULL; }
if (ret < 0)
return ret;
av_strlcatf(buff, size, "a=rtpmap:%d theora/90000\r\n" av_strlcatf(buff, size, "a=rtpmap:%d theora/90000\r\n"
"a=fmtp:%d delivery-method=inline; " "a=fmtp:%d delivery-method=inline; "
@ -682,7 +722,7 @@ static char *sdp_write_media_attributes(char *buff, int size, const AVStream *st
break; break;
default: default:
av_log(fmt, AV_LOG_ERROR, "Unsupported pixel format.\n"); av_log(fmt, AV_LOG_ERROR, "Unsupported pixel format.\n");
return NULL; return AVERROR(ENOSYS);
} }
av_strlcatf(buff, size, "a=rtpmap:%d raw/90000\r\n" av_strlcatf(buff, size, "a=rtpmap:%d raw/90000\r\n"
@ -760,10 +800,10 @@ static char *sdp_write_media_attributes(char *buff, int size, const AVStream *st
av_free(config); av_free(config);
return buff; return 0;
} }
void ff_sdp_write_media(char *buff, int size, const AVStream *st, int idx, int ff_sdp_write_media(char *buff, int size, const AVStream *st, int idx,
const char *dest_addr, const char *dest_type, const char *dest_addr, const char *dest_type,
int port, int ttl, AVFormatContext *fmt) int port, int ttl, AVFormatContext *fmt)
{ {
@ -786,7 +826,7 @@ void ff_sdp_write_media(char *buff, int size, const AVStream *st, int idx,
av_strlcatf(buff, size, "b=AS:%"PRId64"\r\n", p->bit_rate / 1000); av_strlcatf(buff, size, "b=AS:%"PRId64"\r\n", p->bit_rate / 1000);
} }
sdp_write_media_attributes(buff, size, st, payload_type, fmt); return sdp_write_media_attributes(buff, size, st, payload_type, fmt);
} }
int av_sdp_create(AVFormatContext *ac[], int n_files, char *buf, int size) int av_sdp_create(AVFormatContext *ac[], int n_files, char *buf, int size)
@ -832,10 +872,13 @@ int av_sdp_create(AVFormatContext *ac[], int n_files, char *buf, int size)
ttl = 0; ttl = 0;
} }
for (j = 0; j < ac[i]->nb_streams; j++) { for (j = 0; j < ac[i]->nb_streams; j++) {
ff_sdp_write_media(buf, size, ac[i]->streams[j], index++, int ret = ff_sdp_write_media(buf, size, ac[i]->streams[j], index++,
dst[0] ? dst : NULL, dst_type, dst[0] ? dst : NULL, dst_type,
(port > 0) ? port + j * 2 : 0, (port > 0) ? port + j * 2 : 0,
ttl, ac[i]); ttl, ac[i]);
if (ret < 0)
return ret;
if (port <= 0) { if (port <= 0) {
av_strlcatf(buf, size, av_strlcatf(buf, size,
"a=control:streamid=%d\r\n", i + j); "a=control:streamid=%d\r\n", i + j);
@ -864,9 +907,10 @@ int av_sdp_create(AVFormatContext *ac[], int n_files, char *buf, int size)
return AVERROR(ENOSYS); return AVERROR(ENOSYS);
} }
void ff_sdp_write_media(char *buff, int size, const AVStream *st, int idx, int ff_sdp_write_media(char *buff, int size, const AVStream *st, int idx,
const char *dest_addr, const char *dest_type, const char *dest_addr, const char *dest_type,
int port, int ttl, AVFormatContext *fmt) int port, int ttl, AVFormatContext *fmt)
{ {
return AVERROR(ENOSYS);
} }
#endif #endif

Loading…
Cancel
Save