源码地址
https://github.com/979451341/RtmpCamera/tree/master

配置RMTP服务器,虽然之前说了,这里就直接粘贴过来吧

1.配置RTMP服务器

这个我不多说贴两个博客分别是在mac和windows环境上的,大家跟着弄
MAC搭建RTMP服务器
https://www.jianshu.com/p/6fcec3b9d644
这个是在windows上的,RTMP服务器搭建(crtmpserver和nginx)

https://www.jianshu.com/p/c71cc39f72ec

2.关于推流输出的ip地址我好好说说

我这里是手机开启热点,电脑连接手机,这个RTMP服务器的推流地址有localhost,服务器在电脑上,对于电脑这个localhost是127.0.0.1,但是对于外界比如手机,你不能用localhost,而是用这个电脑的在这个热点也就是局域网的ip地址,不是127.0.0.1这个只代表本设备节点的ip地址,这个你需要去手机设置——》更多——》移动网络共享——》便携式WLAN热点——》管理设备列表,就可以看到电脑的局域网ip地址了

3.代码

我们这里要用到SurfaceView和Camera这对老组合,多而不说,就是Camera的配置有的需要注意

                    Camera.Parameters parameters = camera.getParameters();
//对拍照参数进行设置
for (Camera.Size size : parameters.getSupportedPictureSizes()) {
LogUtils.d(size.width + " " + size.height);
}

注意这段打印出来的宽高,后来设置Camera拍摄的图片大小配置必须是里面的一组,否则无法获取Camera的回调数据,这个很关键

parameters.setPictureSize(screenWidth, screenHeight); // 设置照片的大小

还有cpp文件里的宽高也要这样,否则程序会崩溃,其实这里的宽高我们可以通过比例缩放来处理,就可以任意使用宽高,但是我这里没有写。。。。。。。。。

int width = 320;
int height = 240;

Camera预览回调

camera.setPreviewCallback(new StreamIt()); // 设置回调的类

我们在这个回调里传送需要进行推流的数据,这里通过isPlaying标识符控制了,需要我们点击start按钮才会开始推流,并且这里传输数据的代码是通过开启一个单线程来完成,保证上次操作完成了才会执行下一次

    public class StreamIt implements Camera.PreviewCallback {
@Override
public void onPreviewFrame(final byte[] data, Camera camera) {
if(isPlaying){
long endTime = System.currentTimeMillis();
executor.execute(new Runnable() {
@Override
public void run() {
encodeTime = System.currentTimeMillis();
FFmpegHandle.getInstance().onFrameCallback(data);
LogUtils.w("编码第:" + (encodeCount++) + "帧,耗时:" + (System.currentTimeMillis() - encodeTime));
}
});
LogUtils.d("采集第:" + (++count) + "帧,距上一帧间隔时间:"
+ (endTime - previewTime) + " " + Thread.currentThread().getName());
previewTime = endTime;
} }
}

之前还执行了initVideo函数,初始化了FFmpeg并传输了推流地址

计算编码出的yuv数据的大小

    yuv_width = width;
yuv_height = height;
y_length = width * height;
uv_length = width * height / 4;

初始化组件和输出编码环境

    av_register_all();

    //output initialize
avformat_alloc_output_context2(&ofmt_ctx, NULL, "flv", out_path);
//output encoder initialize
pCodec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!pCodec) {
loge("Can not find encoder!\n");
return -1;
}

配置编码环境

    pCodecCtx = avcodec_alloc_context3(pCodec);
//编码器的ID号,这里为264编码器,可以根据video_st里的codecID 参数赋值
pCodecCtx->codec_id = pCodec->id;
//像素的格式,也就是说采用什么样的色彩空间来表明一个像素点
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
//编码器编码的数据类型
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
//编码目标的视频帧大小,以像素为单位
pCodecCtx->width = width;
pCodecCtx->height = height;
pCodecCtx->framerate = (AVRational) {fps, 1};
//帧率的基本单位,我们用分数来表示,
pCodecCtx->time_base = (AVRational) {1, fps};
//目标的码率,即采样的码率;显然,采样码率越大,视频大小越大
pCodecCtx->bit_rate = 400000;
//固定允许的码率误差,数值越大,视频越小
// pCodecCtx->bit_rate_tolerance = 4000000;
pCodecCtx->gop_size = 50;
/* Some formats want stream headers to be separate. */
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
pCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER; //H264 codec param
// pCodecCtx->me_range = 16;
//pCodecCtx->max_qdiff = 4;
pCodecCtx->qcompress = 0.6;
//最大和最小量化系数
pCodecCtx->qmin = 10;
pCodecCtx->qmax = 51;
//Optional Param
//两个非B帧之间允许出现多少个B帧数
//设置0表示不使用B帧
//b 帧越多,图片越小
pCodecCtx->max_b_frames = 0; if (pCodecCtx->codec_id == AV_CODEC_ID_H264) {
// av_dict_set(¶m, "preset", "slow", 0);
/**
* 这个非常重要,如果不设置延时非常的大
* ultrafast,superfast, veryfast, faster, fast, medium
* slow, slower, veryslow, placebo. 这是x264编码速度的选项
*/
av_dict_set(¶m, "preset", "superfast", 0);
av_dict_set(¶m, "tune", "zerolatency", 0);
}

打开编码器

    if (avcodec_open2(pCodecCtx, pCodec, ¶m) < 0) {
loge("Failed to open encoder!\n");
return -1;
}

创建并配置一个视频流

    video_st = avformat_new_stream(ofmt_ctx, pCodec);
if (video_st == NULL) {
return -1;
}
video_st->time_base.num = 1;
video_st->time_base.den = fps;
// video_st->codec = pCodecCtx;
video_st->codecpar->codec_tag = 0;
avcodec_parameters_from_context(video_st->codecpar, pCodecCtx);

查看输出url是否有效,并根据输出格式写入文件头

    if (avio_open(&ofmt_ctx->pb, out_path, AVIO_FLAG_READ_WRITE) < 0) {
loge("Failed to open output file!\n");
return -1;
} //Write File Header
avformat_write_header(ofmt_ctx, NULL);

接下来就是处理Camera传送过来的数据

转换数据格式

    jbyte *in = env->GetByteArrayElements(buffer_, NULL);

根据编码器获取缓存图片大小,并创建缓存图片空间

    int picture_size = av_image_get_buffer_size(pCodecCtx->pix_fmt, pCodecCtx->width,
pCodecCtx->height, 1);
uint8_t *buffers = (uint8_t *) av_malloc(picture_size);

将之前创建的缓存图片空间赋予AVFrame

    pFrameYUV = av_frame_alloc();
//将buffers的地址赋给AVFrame中的图像数据,根据像素格式判断有几个数据指针
av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, buffers, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, 1);

转换AVFrame格式,卓摄像头数据为NV21格式,此处将其转换为YUV420P格式

    memcpy(pFrameYUV->data[0], in, y_length); //Y
pFrameYUV->pts = count;
for (int i = 0; i < uv_length; i++) {
//将v数据存到第三个平面
*(pFrameYUV->data[2] + i) = *(in + y_length + i * 2);
//将U数据存到第二个平面
*(pFrameYUV->data[1] + i) = *(in + y_length + i * 2 + 1);
} pFrameYUV->format = AV_PIX_FMT_YUV420P;
pFrameYUV->width = yuv_width;
pFrameYUV->height = yuv_height;

编码AVFrame数据

avcodec_send_frame(pCodecCtx, pFrameYUV);

获取编码后得到的数据

avcodec_receive_packet(pCodecCtx, &enc_pkt);

释放AVFrame

av_frame_free(&pFrameYUV);

对编码后的数据进行配置,设置播放时间等

    enc_pkt.stream_index = video_st->index;
AVRational time_base = ofmt_ctx->streams[0]->time_base;//{ 1, 1000 };
enc_pkt.pts = count * (video_st->time_base.den) / ((video_st->time_base.num) * fps);
enc_pkt.dts = enc_pkt.pts;
enc_pkt.duration = (video_st->time_base.den) / ((video_st->time_base.num) * fps);
__android_log_print(ANDROID_LOG_WARN, "eric",
"index:%d,pts:%lld,dts:%lld,duration:%lld,time_base:%d,%d",
count,
(long long) enc_pkt.pts,
(long long) enc_pkt.dts,
(long long) enc_pkt.duration,
time_base.num, time_base.den);
enc_pkt.pos = -1;

进行推流

av_interleaved_write_frame(ofmt_ctx, &enc_pkt);

释放Camera传输过来的数据

env->ReleaseByteArrayElements(buffer_, in, 0);

最后释放所有资源

    if (video_st)
avcodec_close(video_st->codec);
if (ofmt_ctx) {
avio_close(ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
ofmt_ctx = NULL;
}

4.VLC的使用

在进行推流时,输入推流地址,观看推流数据,效果如下

Android 音视频深入 十六 FFmpeg 推流手机摄像头,实现直播 (附源码下载)的更多相关文章

  1. Android 音视频深入 十九 使用ijkplayer做个视频播放器(附源码下载)

    项目地址https://github.com/979451341/Myijkplayer 前段时候我觉得FFmpeg做个视频播放器好难,虽然播放上没问题,但暂停还有通过拖动进度条来设置播放进度,这些都 ...

  2. Android 音视频深入 一 AudioRecord录音生成pcm转换为wav(附源码下载)

    本篇项目地址,名字是AudioRecord录音(能暂停,将pch转换为wav),求starhttps://github.com/979451341/Audio-and-video-learning-m ...

  3. Android 音视频深入 十五 FFmpeg 推流mp4文件(附源码下载)

    源码地址https://github.com/979451341/Rtmp 1.配置RTMP服务器 这个我不多说贴两个博客分别是在mac和windows环境上的,大家跟着弄 MAC搭建RTMP服务器h ...

  4. Android 音视频深入 十四 FFmpeg与OpenSL ES 播放mp3音乐,能暂停(附源码下载)

    项目地址https://github.com/979451341/FFmpegOpenslES 这次说的是FFmpeg解码mp3,数据给OpenSL ES播放,并且能够暂停. 1.创建引擎 slCre ...

  5. Android 音视频深入 十二 FFmpeg视频替换声音(附源码下载)

    项目地址,求starhttps://github.com/979451341/AudioVideoStudyCodeTwo/tree/master/FFmpeg%E7%BB%99%E8%A7%86%E ...

  6. Android 音视频深入 十八 FFmpeg播放视频,有声音(附源码下载)

    项目地址https://github.com/979451341/AudioVideoStudyCodeTwo/tree/master/FFmpegv%E6%92%AD%E6%94%BE%E8%A7% ...

  7. 第十六节、特征描述符BRIEF(附源码)

    我们已经知道SIFT算法采用128维的特征描述子,由于描述子用的是浮点数,所以它将会占用512字节的空间.类似的SUFR算法,一般采用64维的描述子,它将占用256字节的空间.如果一幅图像中有1000 ...

  8. Android中Canvas绘图基础详解(附源码下载) (转)

    Android中Canvas绘图基础详解(附源码下载) 原文链接  http://blog.csdn.net/iispring/article/details/49770651   AndroidCa ...

  9. Android网络:HTTP之利用HttpURLConnection访问网页、获取网络图片实例 (附源码)

    http://blog.csdn.net/yanzi1225627/article/details/22222735 如前文所示的TCP局域网传送东西,除了对传输层的TCP/UDP支持良好外,Andr ...

随机推荐

  1. Python脚本备份

    #!/usr/bin/python3 # -*- coding:utf- -*- # 保证源程序可以输入汉字 print bool([]) # 任何为零的数字或空集(空列表.空元组和空字典等)均为Fa ...

  2. ORACLE中INSERT插入多条数据

    insert ALL into u_role_permission(rid,pid) values (4,12) into u_role_permission(rid,pid) values (3,4 ...

  3. 【转载】系统吞吐量(TPS)、用户并发量、性能测试概念和公式

    系统吞度量要素 一个系统的吞度量(承压能力)与request对CPU的消耗.外部接口.IO等等紧密关联.单个reqeust 对CPU消耗越高,外部系统接口.IO影响速度越慢,系统吞吐能力越低,反之越高 ...

  4. 【Git】Git使用记录: remove *.lock eg: index.lock/head.lock

    问题 Another git process seems to be running in this repository, e.g. an editor opened by 'git commit' ...

  5. 关于djangorestframework相关源码分析

    CBV APIView Request 局部全局钩子 认证组件 权限组件 频率组件 分页器组件

  6. CentOS 使用firewalld打开防火墙与端口

    CentOS 使用firewalld打开防火墙与端口 LinuxCentOS 基本使用 启动 : systemctl start firewalld 关闭 : systemctl stop firew ...

  7. async/await处理异步

    async函数返回一个Promise对象,可以使用then方法添加回调函数.当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句. 看代码: 指定多少毫秒后输 ...

  8. vue--transition-group

    1.为什么要使用<transition-group> <transition></transition>是vue封装的过渡组件 <transition nam ...

  9. 使用JSR-303进行校验

    package com.ieou.comac.module.web.dto.employee; import lombok.Data; import javax.validation.constrai ...

  10. bean 装配

    1.装配方式 (1)在xml进行显式装配 (2)在java中进行显式装配 (3)隐式的bean发现机制和自动装配 2.装配方式(3)实现 (1)创建bean /** * @component告诉spr ...