From 21535faea6e1a1a8c717ced934bad2a8568c213a Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 1 Mar 2019 12:11:04 -0500 Subject: [PATCH] TT#50652 support playing back media from memory stream Change-Id: I400c4edbda4633abcff07319ce8cd0609b0021b1 --- daemon/call_interfaces.c | 5 ++ daemon/media_player.c | 104 +++++++++++++++++++++++++++++++++++++- include/media_player.h | 5 ++ utils/rtpengine-ng-client | 23 +++++++-- 4 files changed, 131 insertions(+), 6 deletions(-) diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index e25edebbb..70f301551 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -1763,6 +1763,11 @@ const char *call_play_media_ng(bencode_item_t *input, bencode_item_t *output) { if (media_player_play_file(monologue->player, &file)) goto out; } + else if (bencode_dictionary_get_str(input, "blob", &file)) { + err = "Failed to start media playback from blob"; + if (media_player_play_blob(monologue->player, &file)) + goto out; + } else goto out; diff --git a/daemon/media_player.c b/daemon/media_player.c index 9ee1260f1..e1d3cef4f 100644 --- a/daemon/media_player.c +++ b/daemon/media_player.c @@ -14,6 +14,10 @@ +#define DEFAULT_AVIO_BUFSIZE 4096 + + + static struct timerthread media_player_thread; @@ -22,8 +26,7 @@ static struct timerthread media_player_thread; static void media_player_shutdown(struct media_player *mp) { ilog(LOG_DEBUG, "shutting down media_player"); timerthread_obj_deschedule(&mp->tt_obj); - avformat_free_context(mp->fmtctx); - mp->fmtctx = NULL; + avformat_close_input(&mp->fmtctx); mp->media = NULL; if (mp->handler) codec_handler_free(mp->handler); @@ -31,6 +34,15 @@ static void media_player_shutdown(struct media_player *mp) { if (mp->ssrc_out) obj_put(&mp->ssrc_out->parent->h); mp->ssrc_out = NULL; + if (mp->avioctx) { + if (mp->avioctx->buffer) + av_freep(&mp->avioctx->buffer); + av_freep(&mp->avioctx); + } + if (mp->blob) + free(mp->blob); + mp->blob = NULL; + mp->read_pos = STR_NULL; } @@ -239,8 +251,96 @@ int media_player_play_file(struct media_player *mp, const str *file) { media_player_play_start(mp); + return 0; +} + + +static int __mp_avio_read_wrap(void *opaque, uint8_t *buf, int buf_size) { + struct media_player *mp = opaque; + if (buf_size < 0) + return AVERROR(EINVAL); + if (buf_size == 0) + return 0; + if (!mp->read_pos.len) + return AVERROR_EOF; + + int len = buf_size; + if (len > mp->read_pos.len) + len = mp->read_pos.len; + memcpy(buf, mp->read_pos.s, len); + str_shift(&mp->read_pos, len); + return len; +} +static int __mp_avio_read(void *opaque, uint8_t *buf, int buf_size) { + ilog(LOG_DEBUG, "__mp_avio_read(%i)", buf_size); + int ret = __mp_avio_read_wrap(opaque, buf, buf_size); + ilog(LOG_DEBUG, "__mp_avio_read(%i) = %i", buf_size, ret); + return ret; +} +static int64_t __mp_avio_seek_set(struct media_player *mp, int64_t offset) { + ilog(LOG_DEBUG, "__mp_avio_seek_set(%" PRIi64 ")", offset); + if (offset < 0) + return AVERROR(EINVAL); + mp->read_pos = *mp->blob; + if (str_shift(&mp->read_pos, offset)) + return AVERROR_EOF; + return offset; +} +static int64_t __mp_avio_seek(void *opaque, int64_t offset, int whence) { + ilog(LOG_DEBUG, "__mp_avio_seek(%" PRIi64 ", %i)", offset, whence); + struct media_player *mp = opaque; + if (whence == SEEK_SET) + return __mp_avio_seek_set(mp, offset); + if (whence == SEEK_CUR) + return __mp_avio_seek_set(mp, ((int64_t) (mp->read_pos.s - mp->blob->s)) + offset); + if (whence == SEEK_END) + return __mp_avio_seek_set(mp, ((int64_t) mp->blob->len) + offset); + return AVERROR(EINVAL); +} + +// call->master_lock held in W +int media_player_play_blob(struct media_player *mp, const str *blob) { + const char *err; + + if (media_player_play_init(mp)) + return -1; + + mp->blob = str_dup(blob); + err = "out of memory"; + if (!mp->blob) + goto err; + mp->read_pos = *mp->blob; + + err = "could not allocate AVFormatContext"; + mp->fmtctx = avformat_alloc_context(); + if (!mp->fmtctx) + goto err; + + void *avio_buf = av_malloc(DEFAULT_AVIO_BUFSIZE); + err = "failed to allocate AVIO buffer"; + if (!avio_buf) + goto err; + + mp->avioctx = avio_alloc_context(avio_buf, DEFAULT_AVIO_BUFSIZE, 0, mp, __mp_avio_read, + NULL, __mp_avio_seek); + err = "failed to allocate AVIOContext"; + if (!mp->avioctx) + goto err; + + mp->fmtctx->pb = mp->avioctx; + + // consumes allocated mp->fmtctx + int ret = avformat_open_input(&mp->fmtctx, "dummy", NULL, NULL); + if (ret < 0) + return -1; + + media_player_play_start(mp); return 0; + +err: + ilog(LOG_ERR, "Failed to start media playback from memory: %s", err); + return -1; } diff --git a/include/media_player.h b/include/media_player.h index 742b9b902..d43a7b41d 100644 --- a/include/media_player.h +++ b/include/media_player.h @@ -32,11 +32,16 @@ struct media_player { struct codec_handler *handler; struct ssrc_ctx *ssrc_out; unsigned long seq; + + AVIOContext *avioctx; + str *blob; + str read_pos; }; struct media_player *media_player_new(struct call_monologue *); int media_player_play_file(struct media_player *, const str *); +int media_player_play_blob(struct media_player *, const str *); void media_player_stop(struct media_player *); void media_player_init(void); diff --git a/utils/rtpengine-ng-client b/utils/rtpengine-ng-client index 9a3a5bfbd..2065346e3 100755 --- a/utils/rtpengine-ng-client +++ b/utils/rtpengine-ng-client @@ -67,6 +67,8 @@ GetOptions( 'fragment' => \$options{'fragment'}, 'original-sendrecv' => \$options{'original sendrecv'}, 'file=s' => \$options{'file'}, + 'blob=s' => \$options{'blob'}, + 'blob-file=s' => \$options{'blob-file'}, ) or die; my $cmd = shift(@ARGV) or die; @@ -107,14 +109,20 @@ if ($options{'flags'} && @{$options{'flags'}}) { push(@{$packet{flags}}, @{$options{'flags'}}); } +sub slurp_file { + local $/ = undef; + open(my $fh, '<', $_[0]) or die $!; + my $ret = <$fh>; + die $! unless defined $ret; + close($fh); + return $ret; +} + if (defined($options{sdp})) { $packet{sdp} = $options{sdp}; } elsif (defined($options{'sdp-file'})) { - open(my $fh, '<', $options{'sdp-file'}) or die $!; - my @sdp = <$fh> or die $!; - close($fh); - $packet{sdp} = join('', @sdp); + $packet{sdp} = slurp_file($options{'sdp-file'}); } #elsif (@ARGV && $ARGV[0] eq 'sdp') { # shift(@ARGV); @@ -134,6 +142,13 @@ elsif (defined($options{'sdp-file'})) { # $packet{sdp} = $sdp; #} +if (defined($options{blob})) { + $packet{blob} = $options{blob}; +} +elsif (defined($options{'blob-file'})) { + $packet{blob} = slurp_file($options{'blob-file'}); +} + $options{verbose} and print Dumper \%packet; my $engine = NGCP::Rtpengine->new($options{'proxy-address'}, $options{'proxy-port'});