RTMP直播推流Video(视频)

本文介绍了H264编码中的SPS和PPS序列参数集的封装格式,并给出了具体的实现代码示例。针对不同的帧类型,文章详细解释了关键帧与非关键帧的数据组织方式,包括额外字节的增加及其在编码过程中的作用。

SPS、PPS  的头部信息        增加了额外的16字节的长度 sps/pps 按照关键帧处理

frame type :                                                           关键帧    /  非关键帧                           (4 bit)     
CodecID : 7表示AVC                                    CodecID 和frametype组合成一个字节        (4 bit)    0x17 / 0x27


fixed : 0x00(AVCDecoderConfigurationRecord) 0x00 0x00 0x00 (4 byte)


sps + pps数据
configurationVersion (1 byte)        0x01 版本
AVCProfileIndication (1 byte)        sps[1] Profile
profile_compatibility (1 byte)        sps[2] 兼容性
AVCLevelIndication (1 byte)        sps[3] Profile level
lengthSizeMinusOne (1 byte)        0xff 包长数据所使用的字节数
sps number (1 byte)            0xe1 sps个数
sps data length (2 byte)            sps长度
sps data                    sps实际内容
pps number (1 byte)            0x01 pps的个数
pps data length (2 byte)            pps长度
pps data                    pps内容

char *body = packet->m_body;

    int i = 0;
    //0x17关键帧    frame type (4 bit)
    //              CodecID   (4 bit)
    body[i++] = 0x17;

    //fixed     (4 byte)
    body[i++] = 0x00;
    body[i++] = 0x00;
    body[i++] = 0x00;
    body[i++] = 0x00;

    //版本 Profile 兼容性 ProfileLevel
    body[i++] = 0x01;
    body[i++] = sps[1];
    body[i++] = sps[2];
    body[i++] = sps[3];

    //包长数据所使用的字节数
    body[i++] = 0xff;
    //sps个数
    body[i++] = 0xe1;
    //sps长度 (2 byte)
    body[i++] = (sps_len >> 8) & 0xff;
    body[i++] = sps_len & 0xff;
    //sps实际内容
    memcpy(&body[i], sps, sps_len);
    i += sps_len;

    //pps的个数
    body[i++] = 0x01;
    //pps长度 (2 byte)
    body[i++] = (pps_len >> 8) & 0xff;
    body[i++] = pps_len & 0xff;
    //pps实际内容
    memcpy(&body[i], pps, pps_len);

H264      增加了额外的9字节的长度

frame type : 1关键帧、2非关键帧 (4 bit)     
CodecID : 7表示AVC (4 bit)    0x17/0x27 和frametype组合成一个字节

fixed : 0x01(NALU) 0x00 0x00 0x00 (4 byte)
data length : 长度信息(4 byte)
data : h264裸数据

char *body = packet->m_body;
    int i = 0;

    //0x17关键帧    frame type (4 bit)
    //0x27非关键帧  CodecID   (4 bit)
    if (keyFrame) {
        body[i++] = 0x17;
    } else {
        body[i++] = 0x27;
    }

    //fixed     (4 byte) NALU
    body[i++] = 0x01;
    body[i++] = 0x00;
    body[i++] = 0x00;
    body[i++] = 0x00;

    //dataLength : 长度信息(4 byte)
    body[i++] = (data_len >> 24) & 0xff;
    body[i++] = (data_len >> 16) & 0xff;
    body[i++] = (data_len >> 8) & 0xff;
    body[i++] = data_len & 0xff;
    //h264 裸数据
    memcpy(&body[i], data, data_len);

LivePushActivity 

package com.example.glivepush;

import android.os.Bundle;
import android.os.Environment;
import android.se.omapi.SEService;
import android.util.Log;
import android.view.View;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import com.example.glivepush.camera.GCameraView;
import com.example.glivepush.push.BasePushEncoder;
import com.example.glivepush.push.GConnectListener;
import com.example.glivepush.push.PushEncodec;
import com.example.glivepush.push.PushVideo;
import com.example.glivepush.util.DisplayUtil;

public class LivePushActivity extends AppCompatActivity {

    private PushVideo pushVideo;

    private GCameraView gCameraView;
    private boolean start = false;
    private PushEncodec pushEncodec;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_live_push);
        pushVideo = new PushVideo();

        gCameraView = findViewById(R.id.cameraView);

        pushVideo.setgConnectListener(new GConnectListener() {
            @Override
            public void onConnecting() {
                Log.d("godv", "链接服务器中");
            }

            @Override
            public void onConnectSuccess() {
                Log.d("godv", "链接服务器成功");

                pushEncodec = new PushEncodec(LivePushActivity.this, gCameraView.getTextureId());
                pushEncodec.initEncodec(
                        gCameraView.getEglContext(),
                        DisplayUtil.getScreenWidth(LivePushActivity.this),
                        DisplayUtil.getScreenHeight(LivePushActivity.this),
                        44100,
                        2
                );
                pushEncodec.startRecord();

                /*************************************直播推流-video-start***********************************/
                pushEncodec.setOnMediaInfoListener(new BasePushEncoder.OnMediaInfoListener() {
                    @Override
                    public void onMediaTime(int times) {

                    }

                    @Override
                    public void onSPSPPSInfo(byte[] sps, byte[] pps) {
                        pushVideo.pushSPSPPS(sps, pps);
                    }

                    @Override
                    public void videoInfo(byte[] data, boolean keyFrame) {
                        pushVideo.pushVideoData(data, keyFrame);
                    }
                });
                /*************************************直播推流-video-end***********************************/

            }

            @Override
            public void onConnectFail(String msg) {
                Log.d("godv", msg);
            }
        });
    }

    public void startPush(View view) {
        start = !start;

        if (start) {
            pushVideo.initLivePush("rtmp://192.168.0.14/myapp/mystream");
        } else {
            if (pushEncodec != null) {
                pushEncodec.stopRecord();
                pushEncodec = null;
            }
        }
    }
}

BasePushEncoder

package com.example.glivepush.push;

import android.content.Context;
import android.media.MediaCodec;
import android.media.Me
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值