diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 528cdcd314..49d65f0372 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -20,6 +20,7 @@ */ #include "avformat.h" #include "rtp_internal.h" +#include "rdt.h" #define REGISTER_MUXER(X,x) { \ extern AVOutputFormat x##_muxer; \ @@ -165,6 +166,7 @@ void av_register_all(void) REGISTER_DEMUXER (SDP, sdp); #ifdef CONFIG_SDP_DEMUXER av_register_rtp_dynamic_payload_handlers(); + av_register_rdt_dynamic_payload_handlers(); #endif REGISTER_DEMUXER (SEGAFILM, segafilm); REGISTER_DEMUXER (SHORTEN, shorten); diff --git a/libavformat/rdt.c b/libavformat/rdt.c index 1d0e4559d0..e5a1f89c6d 100644 --- a/libavformat/rdt.c +++ b/libavformat/rdt.c @@ -27,12 +27,19 @@ #include "avformat.h" #include "libavutil/avstring.h" +#include "rtp_internal.h" #include "rdt.h" #include "libavutil/base64.h" #include "libavutil/md5.h" #include "rm.h" #include "internal.h" +typedef struct rdt_data { + AVFormatContext *rmctx; + uint8_t *mlti_data; + unsigned int mlti_data_size; +} rdt_data; + void ff_rdt_calc_response_and_checksum(char response[41], char chksum[9], const char *challenge) @@ -71,3 +78,132 @@ ff_rdt_calc_response_and_checksum(char response[41], char chksum[9], chksum[i] = response[i * 4]; chksum[8] = 0; } + +static int +rdt_load_mdpr (rdt_data *rdt, AVStream *st, int rule_nr) +{ + ByteIOContext *pb; + int size; + uint32_t tag; + + /** + * Layout of the MLTI chunk: + * 4:MLTI + * 2: + * Then for each stream ([number_of_streams] times): + * 2: + * 2: + * Then for each mdpr chunk ([number_of_mdpr_chunks] times): + * 4: + * [size]: + * we skip MDPR chunks until we reach the one of the stream + * we're interested in, and forward that ([size]+[data]) to + * the RM demuxer to parse the stream-specific header data. + */ + if (!rdt->mlti_data) + return -1; + url_open_buf(&pb, rdt->mlti_data, rdt->mlti_data_size, URL_RDONLY); + tag = get_le32(pb); + if (tag == MKTAG('M', 'L', 'T', 'I')) { + int num, chunk_nr; + + /* read index of MDPR chunk numbers */ + num = get_be16(pb); + if (rule_nr < 0 || rule_nr >= num) + return -1; + url_fskip(pb, rule_nr * 2); + chunk_nr = get_be16(pb); + url_fskip(pb, (num - 1 - rule_nr) * 2); + + /* read MDPR chunks */ + num = get_be16(pb); + if (chunk_nr >= num) + return -1; + while (chunk_nr--) + url_fskip(pb, get_be32(pb)); + size = get_be32(pb); + } else { + size = rdt->mlti_data_size; + url_fseek(pb, 0, SEEK_SET); + } + rdt->rmctx->pb = pb; + if (ff_rm_read_mdpr_codecdata(rdt->rmctx, st, size) < 0) + return -1; + + url_close_buf(pb); + return 0; +} + +static unsigned char * +rdt_parse_b64buf (unsigned int *target_len, const char *p) +{ + unsigned char *target; + int len = strlen(p); + if (*p == '\"') { + p++; + len -= 2; /* skip embracing " at start/end */ + } + *target_len = len * 3 / 4; + target = av_mallocz(*target_len + FF_INPUT_BUFFER_PADDING_SIZE); + av_base64_decode(target, p, *target_len); + return target; +} + +static int +rdt_parse_sdp_line (AVStream *stream, void *d, const char *line) +{ + rdt_data *rdt = d; + const char *p = line; + + if (av_strstart(p, "OpaqueData:buffer;", &p)) { + rdt->mlti_data = rdt_parse_b64buf(&rdt->mlti_data_size, p); + rdt_load_mdpr(rdt, stream, 0); + } else if (av_strstart(p, "StartTime:integer;", &p)) + stream->first_dts = atoi(p); + + return 0; +} + +static void * +rdt_new_extradata (void) +{ + rdt_data *rdt = av_mallocz(sizeof(rdt_data)); + + av_open_input_stream(&rdt->rmctx, NULL, "", &rdt_demuxer, NULL); + + return rdt; +} + +static void +rdt_free_extradata (void *d) +{ + rdt_data *rdt = d; + + if (rdt->rmctx) + av_close_input_stream(rdt->rmctx); + av_freep(&rdt->mlti_data); + av_free(rdt); +} + +#define RDT_HANDLER(n, s, t) \ +static RTPDynamicProtocolHandler ff_rdt_ ## n ## _handler = { \ + s, \ + t, \ + CODEC_ID_NONE, \ + rdt_parse_sdp_line, \ + rdt_new_extradata, \ + rdt_free_extradata \ +}; + +RDT_HANDLER(live_video, "x-pn-multirate-realvideo-live", CODEC_TYPE_VIDEO); +RDT_HANDLER(live_audio, "x-pn-multirate-realaudio-live", CODEC_TYPE_AUDIO); +RDT_HANDLER(video, "x-pn-realvideo", CODEC_TYPE_VIDEO); +RDT_HANDLER(audio, "x-pn-realaudio", CODEC_TYPE_AUDIO); + +void av_register_rdt_dynamic_payload_handlers(void) +{ + ff_register_dynamic_payload_handler(&ff_rdt_video_handler); + ff_register_dynamic_payload_handler(&ff_rdt_audio_handler); + ff_register_dynamic_payload_handler(&ff_rdt_live_video_handler); + ff_register_dynamic_payload_handler(&ff_rdt_live_audio_handler); +} diff --git a/libavformat/rdt.h b/libavformat/rdt.h index f7aad6dfc9..5da9a78628 100644 --- a/libavformat/rdt.h +++ b/libavformat/rdt.h @@ -37,4 +37,9 @@ void ff_rdt_calc_response_and_checksum(char response[41], char chksum[9], const char *challenge); +/** + * Register RDT-related dynamic payload handlers with our cache. + */ +void av_register_rdt_dynamic_payload_handlers(void); + #endif /* AVFORMAT_RDT_H */ diff --git a/libavformat/rm.h b/libavformat/rm.h index a57e517bab..4ad1c30fef 100644 --- a/libavformat/rm.h +++ b/libavformat/rm.h @@ -63,6 +63,9 @@ typedef struct { int sub_packet_lengths[16]; /// Length of each aac subpacket } RMContext; +/*< input format for Realmedia-style RTSP streams */ +extern AVInputFormat rdt_demuxer; + /** * Read the MDPR chunk, which contains stream-specific codec initialization * parameters. diff --git a/libavformat/rmdec.c b/libavformat/rmdec.c index cb3863b8a6..ee68ac1fae 100644 --- a/libavformat/rmdec.c +++ b/libavformat/rmdec.c @@ -795,3 +795,10 @@ AVInputFormat rm_demuxer = { NULL, rm_read_dts, }; + +AVInputFormat rdt_demuxer = { + "rdt", + NULL_IF_CONFIG_SMALL("RDT demuxer"), + sizeof(RMContext), + NULL, NULL, NULL, rm_read_close, NULL, NULL +};