Android在NDK JNI中使用MediaMuxer合成mp4的流程

前言:找了很久,很少有NDK层使用合成mp4的例程,由于我们使用的是实时流合成mp4的文件,基本所有的视频流都是在C++操作的。因此写个笔记记录一下对实时流合成mp4格式视频。

1、要使用的对象:

	AMediaMuxer *mMediaMuxer = nullptr;
	AMediaFormat *mMediaFormat = nullptr;
	int64_t mMediaMuxerVideoFrameIndex = 0;
	int muxerIndex = -1;

2、创建对象:这里需要注意的是,一定要设置好视频流的vps/sps/pps数据。否则无法生生mp4文件。其中h265只需要使用csd_0。

int MediaCodec_MuxerCreateMp4(const char *pathName) {
    mMediaMuxer_fp = fopen(pathName, "wb+");// 打开新建一个文件。
    if (mMediaMuxer_fp == nullptr) {
        LOGCATE("MediaCodecMuxer:: Mp4 file fopen err!");
        return -1;
    }

    mMediaMuxer_fd = fileno(mMediaMuxer_fp); // 由于muexr的原因,这里需要转换一下。
    if (mMediaMuxer_fd < 0) {
        perror("mp4 file err: ");
        LOGCATE("MediaCodecMuxer:: Mp4 file open err! = %d", mMediaMuxer_fd);
    }

    // 设置mp4输出的格式。
    mMediaFormat = AMediaFormat_new();
    AMediaFormat_setInt32(mMediaFormat, AMEDIAFORMAT_KEY_WIDTH, RecordWidth);
    AMediaFormat_setInt32(mMediaFormat, AMEDIAFORMAT_KEY_HEIGHT, RecordHeight);
    AMediaFormat_setInt32(mMediaFormat, AMEDIAFORMAT_KEY_FRAME_RATE, RecordFrameRate);
    if (format == H265_FORMAT) {
        AMediaFormat_setString(mMediaFormat, AMEDIAFORMAT_KEY_MIME, "video/hevc");
        AMediaFormat_setBuffer(mMediaFormat, AMEDIAFORMAT_KEY_CSD_0, sdp_info_date, sdp_info_size);
    } else if (format == H264_FORMAT) {
        AMediaFormat_setString(mMediaFormat, AMEDIAFORMAT_KEY_MIME, "video/avc");
        AMediaFormat_setBuffer(mMediaFormat, AMEDIAFORMAT_KEY_CSD_0, sps_buf, sps_len);
        AMediaFormat_setBuffer(mMediaFormat, AMEDIAFORMAT_KEY_CSD_1, pps_buf, pps_len);
    }

    mMediaMuxer = AMediaMuxer_new(mMediaMuxer_fd, AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);// 新建一个复合输出。
    muxerIndex = AMediaMuxer_addTrack(mMediaMuxer, mMediaFormat);// 添加一路视频流。
    AMediaMuxer_start(mMediaMuxer);// 开启复合器输出。
    mMediaMuxerVideoFrameIndex = 0;// 视频帧数初始化。

    LOGCATE("MediaCodecMuxer:: Params FrameRate= %d, FrameRateTimeUs = %ld", RecordFrameRate,
            FrameRateTimeUs);

    // 开启录像线程。
    pthread_mutex_lock(&Mutex_RecordingVideo);
    Write_File_Is_First_IDR_Frame = false; //第一帧要从关键帧开始。
    RecordingVideo_State = RECORDING_STATE_RECORDING;
    pthread_mutex_unlock(&Mutex_RecordingVideo);
    LOGCATE("MediaCodecMuxer:: Start Recording...");
    return 0;
}

3、写入数据:循环写入数据即可。这里写入的数据是h264/h265数据包的。FrameRateTimeUs是按照帧率计算出来的。这个的单位是us。例如25帧的话,则FrameRateTimeUs = 1s/25 = 40000us。这里是口算的,计算错误了请评论纠正。

int MediaCodec_MuxerWriteVideoFrame(const uint8_t *data, int size) {
    if (format == H265_FORMAT) {
        if (data[4] == Muxer_H265_IDR || data[4] == Muxer_H265_P) {
            AMediaCodecBufferInfo info;// 设置视频帧参数。
            info.flags = 1;// 这里NDK没有定义buffer视频帧参数,参照java层参数这里写1
            info.size = size;
            info.offset = 0;
            info.presentationTimeUs = FrameRateTimeUs * (mMediaMuxerVideoFrameIndex++);
            AMediaMuxer_writeSampleData(mMediaMuxer, muxerIndex, data, &info); 
        }
    } else if (format == H264_FORMAT) {
        if (data[4] == Muxer_H264_IDR || data[4] == Muxer_H264_P) {
            AMediaCodecBufferInfo info;
            info.flags = 1;//
            info.size = size;
            info.offset = 0;
            info.presentationTimeUs = FrameRateTimeUs * (mMediaMuxerVideoFrameIndex++);
            AMediaMuxer_writeSampleData(mMediaMuxer, muxerIndex, data, &info); 
        }
    }

    return 0;
}

4、写入结束标志位,这里偷懒,直接写入一个空的数据包就可以了。如果不写这个结束包的话,保存的视频也可以播放,但是log会报一个录入音视频流没有正确结束的错误:mpeg4writer: stop() called but track is not started or stopped。

int MediaCodec_MuxerWriteEndFrame() {
    uint8_t buf[5] = {0X00, 0X00, 0X00, 0X01, 0X00};
    AMediaCodecBufferInfo info;
    info.flags = AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
    info.size = 0;
    info.offset = 0;
    info.presentationTimeUs = FrameRateTimeUs * (mMediaMuxerVideoFrameIndex++);
    AMediaMuxer_writeSampleData(mMediaMuxer, muxerIndex, buf, &info); 
    return 0;
}

5、结束录像:其实也就是释放资源。其中muexr的释放,最好先stop(),再删除。

void MediaCodec_MuxerCloseMp4() {
    MediaCodec_MuxerWriteEndFrame();

    AMediaFormat_delete(mMediaFormat);

    AMediaMuxer_stop(mMediaMuxer);
    AMediaMuxer_delete(mMediaMuxer);

    mMediaMuxer = nullptr;
    mMediaFormat = nullptr;

    fclose(mMediaMuxer_fp);
    mMediaMuxer_fp = nullptr;

    close(mMediaMuxer_fd);
    mMediaMuxer_fd = -1;
}

另外:这里只有视频部分,因为目前处理的流只有视频。音频部分大概也是在视频轨道的基础上再加一个音频轨道,如果有大佬在此基础上增加,可以A一下我,多谢了。大概流程就是这样了。全部代码由于都是字面意思,注释就不写那么详细了。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值