android recorder通过rtp发送h264视频数据给vlc播放
android player通过rtp协议接收h264视频数据播放
Android recorder通过rtp发送aac数据给vlc播放
Android player实现播放来自rtp连接的aac数据的player

开启RTP端口监听

我们在发送端向端口40020发送数据,所以这里监听的是40020端口,payload type是97与发送端设置的一致。

audioRtpWrapper.open(40020, 97, 1000);

设置rtp callback用于接收aac原始数据。

audioRtpWrapper.setCallback { data, len ->
                if (len < 4) return@setCallback
                val index = indexArray.take()
                if (currentTime == 0L) {
                    currentTime = System.currentTimeMillis()
                val buffer = audioDecodeCodec.mediaCodec.getInputBuffer(index)
                val time = (System.currentTimeMillis() - currentTime) * 1000
                if (hasAuHeader) {
                    buffer?.position(0)
                    buffer?.put(data, 4, len - 4);
                    buffer?.position(0)
                    audioDecodeCodec.mediaCodec.queueInputBuffer(index, 0, len - 4, time, 1)
                } else {
                    buffer?.position(0)
                    buffer?.put(data, 0, len);
                    buffer?.position(0)
                    audioDecodeCodec.mediaCodec.queueInputBuffer(index, 0, len, time, 1)

接收到aac数据后直接写入MediaCodec的inputbuffer,这里针对是否有au header来处理数据。如果有au header,那么跳过au header数据。au header length 和 au header共占用4 bytes。在通过index获取inputbuffer的时候要格外注意index的有效性,即index必须是mediacodec释放出来的buffer的index,否则会发送buffer写入错误。

创建MediaCodec解码aac数据

创建MediaCodec的时候要指定采样率、通道数、格式等信息,这些信息需要与发送端保持一致。

            val sampleRate = 44100
            val audioFormat = MediaFormat.createAudioFormat(
                MediaFormat.MIMETYPE_AUDIO_AAC,
                sampleRate,
                audioChannelCount
            audioFormat.setByteBuffer("csd-0", audioSpecificConfig)
            var currentTime = 0L
            indexArray.clear()
            audioDecodeCodec = object : AudioDecodeCodec(audioFormat) {
                override fun onInputBufferAvailable(codec: MediaCodec, index: Int) {
                    indexArray.put(index)

这里的"csd-0"参数要格外的关注,因为这个参数不设置或是设置错误都会在解码过程中发生buffer的读写错误。audioSpecificConfig这个变量的生成规则可以参考下面这段代码:

 private val audioChannelCount = 1;
    private val audioProfile = 1
     *  97000, 88200, 64000, 48000,44100, 32000, 24000, 22050,16000, 12000, 11025, 8000,7350, 0, 0, 0
    private val audioIndex = 4
    private val audioSpecificConfig = ByteArray(2).apply {
        this[0] = ((audioProfile + 1).shl(3).and(0xff)).or(audioIndex.ushr(1).and(0xff)).toByte()
        this[1] = ((audioIndex.shl(7).and(0xff)).or(audioChannelCount.shl(3).and(0xff))).toByte()
    }.let {
        val buffer = ByteBuffer.allocate(2)
        buffer.put(it)
        buffer.position(0)
        buffer

当然如果不设置csd-0这个参数,那么从rtp返回的数据中有config数据也是可以的。但是rtp是广播形式的,它并不知道什么时候需要广播config数据,所以配置csd-0是比较好的选择。(注意:config数据指的是发送端的MediaCodec编码启动后生成的config 数据buffer)

使用AudioTrack播放解码后的音频

首先根据mediaformat信息创建AudioTrack并进行播放,这里设置的参数要与MediaCodec设置的一致,同事这种流模式播放(MODE_STREAM)

    override fun onOutputFormatChanged(codec: MediaCodec, format: MediaFormat) {
        val sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE)
        val channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT)
        val minBufferSize = AudioRecord.getMinBufferSize(sampleRate, if (channelCount == 1) AudioFormat.CHANNEL_IN_MONO else AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
        audioTrack = AudioTrack(AudioManager.STREAM_VOICE_CALL, sampleRate, channelCount, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, MODE_STREAM);
        audioTrack?.play();

从MediaCodec读取解码后的数据并写入AudioTrack,这里采用的是block方式写入。

override fun onOutputBufferAvailable(codec: MediaCodec, index: Int, info: MediaCodec.BufferInfo) {
        Log.d("audio_dragon","onOutputBufferAvailable $index")
        kotlin.runCatching {
            val buffer = codec.getOutputBuffer(index) ?: return;
            buffer.position(info.offset);
            audioTrack?.write(buffer, info.size, WRITE_BLOCKING);
            codec.releaseOutputBuffer(index, false);
 

https://github.com/mjlong123123/AudioPlayer.git

我的公众号已经开通,公众号会同步发布。
欢迎关注我的公众号

在这里插入图片描述

一、接收数据rtp包的组包与拆包已经由rtp 库完成,这里可以从rtp库的回调直接接收到原始发送的数据。我们可以看到rtp payload指定的类型是96,96代表的就是h264视频数据类型。这里监听的是偶数端口40018,奇数端口留给rtcp使用。我们接收到的数据是nalu分片数据,我们还需要把分片数据组成完整的nalu数据。 我有一个Android项目,我需要构建一个客户端应用程序来接收UDP或RTP单播视频流并播放它们。不幸的是,我似乎无法得到这个工作,并已广泛搜索解决方案!Android上的UDP视频流我正在Xoom(Android 3.2)和Nexus S(Android 2.3.6)上进行测试,并且知道他们可以在使用MX Player(第三方媒体播放器应用程序)作为客户端时播放内容,但我无法让本地媒体播放播放... 我已經試圖從播放器( Daroon播放器,來自 PlayStore ) 讀取它,它運行良好,所以我假設我的foolowing問題不是因為廣播。我看到,通過不同的方式向用戶展示視頻內容是可行的:使用一個新的Intent 與 ACTION_VIEW,並且Android選擇一個可以查看內容的應用程序;使用MediaPlayer類和 VideoView 。我有兩個問題,讓我們從最重要的開始: - 對於上述... 因为Android ijkplayer播放rtsp直播流在加载时延迟在5-10秒实在难以接受,再修改后解决了延迟问题,目前已达到2秒甚至秒开的效果 添加的方法:VideoOptionModel videoOptionMode0l = new VideoOptionModel(1, "analyzemaxduration", 100); VideoOptionModel videoOptionMode02 = new VideoOptionModel(1, "probesize", 10240); Video 我几周以来一直是Android开发的新手,我需要编写一个可以向用户显示UDP或RDP多播的实时流的应用程序.该流位于诸如“rtp://230.0.0.11:1234”的地址,由于此模块,WIFI将发出该流:http://www.ikusi.es/public/ctrl_public_prod.php?accion=verProducto&id_familia=34&id_gama=... IjkPlayer播放器其实是支持RTP直播的,但是只能用ffmpeg软解码不能使用硬解码。这里将会从以下几个方面配置使用1、UDP RTP/RTMP区别2、FFMpeg推流3、IjkPlayer编译选项4、RTP硬解码支持一、RTP/RTMP区别RTMP直播是基于TCP协议,UDP RTP是基于UDP协议。也就是说RTMP将会更加可靠,而UDP RTP延时将会更低。二、FFMpeg推流关于HLS... android:id="@+id/surface_view" android:layout_width="250dp" android:layout_height="250dp" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopO. 你创建了一个漂亮的UI,但是它很单调运行这个player可以在进入Activity或者Fragment的时候为view创建漂亮的过渡效果。将它放到Activity的onCreate()或者Fragment的onViewCreated()中。Player.init().animate(headerAction).then().animate(fabAction).then().animate(bot... 在公司一个项目中需要在android端上到媒体服务器上通过rtsp取流,由于媒体服务器统一的去前端网络摄像机取流,导致媒体服务器和和前端网络摄像机处于同一局域网内,项目需要在外网中播放视频,媒体服务器的rtsp端口554在公司路由器中已经成功的映射出去。  项目开发都是在局域网中,取流这些都是正常的视频的延迟也在可接受的范围内,也让朋友(老毛)拿着他的IPhone通过4G和wifi 在vlc上取 数据采集部分使用的是Camera2,CameraHolder是对camera2的简单封装。 Camera2有个显著的优势,他可以同时添加多个surface用于接收camer数据。 下面是通过CameraHolder启动camera的流程: override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ...... 封装的工具类:package com.example.voaactest;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import com.sinaapp.bashell.VoAACEncoder;import ...