源码地址
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. 网页静态化技术--Freemarker入门

    网页静态化技术:为什么要使用网页静态化技术 网页静态化解决方案在实际开发中运用比较多,例如新闻网站,门户网站中的新闻频道或者是文章类的频道. 对于电商网站的商品详细页来说,至少几百万个商品,每个商品又 ...

  2. 【题解】Luogu CF817F MEX Queries

    原题传送门 817,我突然想到了某8位质数 这题珂以说是珂朵莉树的模板 三个操作都肥肠简单,前两个区间赋值,第三个区间0变1,1变0 每次输出从头开始扫描就行(我忘了珂朵莉树的性质,竟然还动态维护最左 ...

  3. FL studio里的项目设置介绍

    FL studio作为具有众多音乐功能,能够制作多轨音频录制,排序和混音的一款专业软件,我们可以借助VST主机,灵活的调音台,高级MIDI和ReWire支持,来创建专业品质的各种音乐曲目. 而今天我们 ...

  4. CF Round #551 (Div. 2) D

    CF Round #551 (Div. 2) D 链接 https://codeforces.com/contest/1153/problem/D 思路 不考虑赋值和贪心,考虑排名. 设\(dp_i\ ...

  5. Java 处理word文档后在前端展示

    最新新开发的这个项目需要使用word文档并要求能在前端页面上带格式展示,由于项目不是内部使用,所以不考虑插件类的处理模式,都必须要本地处理完成,前端不需要做什么更新或者说安装就能直接访问,类似于百度文 ...

  6. Vue-admin工作整理(二):项目结构个人配置

    通过上一篇文章(Vue-admin工作整理(一):项目搭建)操作完毕后,基础项目已经搭建,下面就要对项目本身进行一下项目结构调整来符合自己的项目要求 1.首先要对package.json文件进行调整, ...

  7. Java——ping & telnet实现

    ping & telnet 实现类: import org.springframework.data.web.JsonPath; import java.io.IOException; imp ...

  8. [原][飞行仿真]helios与dcs world安装,详尽教程

    可以参考的博客: http://www.insky.cn/bbs/forum.php?mod=viewthread&tid=58175 http://www.insky.cn/bbs/foru ...

  9. 关于Ocelot和Consul 实现GateWay(网关) 服务注册 负载均衡等方面

    Ocelot   路由  请求聚合  服务发现 认证  鉴权 限流熔断 内置负载均衡器 Consul   自动服务发现    健康检查 通过Ocelot搭建API网关   服务注册   负载均衡 1. ...

  10. git1使用步骤初始化拉取修改提交推送

    Git 使用 git init 命令来初始化一个 Git 仓库,Git 的很多命令都需要在 Git 的仓库中运行,所以 git init 是使用 Git 的第一个命令. 在执行完成 git init  ...