客户端的话最主要就是使用ffmpeg。
接下来要讲的就是从ffmpeg的编译开始,到编码,以及推流,到解码等过程。
ffmpeg的编译
懂英文看这里就行:ubuntu下的编译指南
ffmpeg的编译需要linux环境,我这里使用的是虚拟机(vmware+ubuntu),软件的话大家自己网上下载就行,这里需要注意的就是vmTools的安装,可参考戳这里,如果你不安装vmtools的话就不能直接拖动文件到虚拟机,就很麻烦。总之按照上面的百度经验的方法安装了之后就能直接拖动文件到虚拟机,总之就很方便。
接下来进入正文,首先下载ffmpeg的压缩包,这是ffmpeg的官网,你直接百度ffmpeg也行,进入官网之后直接下载就行(中间最明显的那个Download直接按下去,不要犹豫)。
下载之后进入,蛋疼的编译。。。把压缩包拖到ubuntu中,放在哪个目录中随便,我的话是在桌面建了一个文件夹sqq。然后把压缩包放在sqq下面。
1、打开终端(ctrl+alt+t)或者上级sqq文件夹,右键open in terminal
2、右键的同学直接就tar jxvf ffmpeg-x.x.x.tar.bz2 ,快捷键打开的同学就cd到sqq目录,注意这里解压的命令,可参考:linux下解压命令大全。
上面的工作就完成了解压,其实因为是yoga的ubuntu所以解压可以直接右键 extract here,哈哈哈哈。。。。不要打我!既然用了linux系统就多用一下命令行,没毛病。
解压之后,大家就可以去看看雷神的博客了:最简单的基于FFmpeg的移动端例子:Android HelloWorld
大家可能在其他地方百度到一些在linux下编译ffmpeg的博客,但是很多都不是针对android的,直接编译的在android上无法使用,因为android是arm的cpu,而我们一般的电脑是x86之类的,所以需要交叉编译,也就需要在linux下载用ndk。具体的操作就去看雷神的博客。
1、这里你可能会遇到no working c compiler 的问题,去看下解压出来的文件夹下的config.log,一般是因为ndk的路径写错了,(遇到这个问题,可能有人会去叫你配置环境变量之类的,我告诉你不需要,只要把路径写对就行)。
2、这里简单介绍几个指令的意思
–enable-shared 这是 configure 常用的一个参数,表示启用动态库版本。
如果你要编译一个库的源代码,可以把它编译成静态库,也可以把它编译成动态库。如果你想编译成静态库,就用 –enable-shared参数;如果你想编译成静态库,就用–enable-static参数。动态库是运行时加载,静态库就相当于写在自己的代码中。
–prefix=/usr/local/ffmpeg 指定安装路径
不指定prefix,则可执行文件默认放在/usr /local/bin,库文件默认放在/usr/local/lib,配置文件默认放在/usr/local/etc。其它的资源文件放在/usr /local/share。你要卸载这个程序,要么在原来的make目录下用一次make uninstall(前提是make文件指定过uninstall),要么去上述目录里面把相关的文件一个个手工删掉。
指定prefix,直接删掉一个文件夹就够了。
–disable-yasm 编译FFMPEG时不加的话会出现 ffmpeg yasm not found, use –disable-yasm for a crippled build的错误,是因为 FFMPEG为了提高编译速度,使用了汇编指令,如果系统中没有yasm指令的话,就会出现上述的问题。所以就直接disable就可以了
正常的按照上面的步骤走下来应该就完成了编译工作。
这个时候你会很高兴的去把生成的文件从虚拟机中复制出来,很可能会出现复制不出来的问题,我的方法是直接压缩然后就可以复制了。很简单。
客户端技术要点
完成了编译之后,不能说你已经进入了音视频技术界,但是绝对可以吹逼说自己会linux,是不是很开心。不逼逼了,进入正题讲一讲,怎么使用ffmpeg做客户端的直播。
其实在讲下面的内容之前,大家最好是先有一点音视频编码的基础,这里我就不详细说了,不要问我为什么,我tmd也是个菜逼。
像我们一般做android应用,牵扯到音视频开发,无非就是调用一下系统的api,用的最多的可能是MediaRecorder,底层一点录制音频用AudioRecord,播放用AudioTrack,录制视频用Camera。用MediaRecorder的话其实就是硬编码,用硬编码其实速度应该是最快的,但是牵扯到适配的问题(不同的厂商封装的格式之类的会有区别),所以我这里还是选择使用软编码,也就是自己做编码工作,编码其实就是个压缩的过程,去掉一些不影响质量又不需要的数据,比如声音,人耳能听到的也就20hz~20khz之间,其他的就都是多余数据可以去掉。
不扯了进入正题,我这里也就按照我自己的实现流程去讲好了。不管你理解不理解。
首先android使用Camera采集原始视频数据,android摄像头采集的数据是NV21格式的,需要先转换成YUV420p格式随后使用ffmpeg编码成h264.
其次android使用AudioRecord采集音频数据,采集的音频数据是pcm格式的,可以直接使用ffmpeg编码成aac的。
(上面不懂aac、h264之类的先去补一下音视频知识,不然就不要继续看了,没意义的,兄弟!)
在很多地方看到,在完成了上面的两个步骤之后说还需要做音视频的同步,我这里音视频编码的时候使用的时间戳都是使用的系统的时间,倒是没有出现播放的时候不同步的情况,但是出现了加速的问题,单独视频或者音频倒是对的,说明还是需要做同步,我的项目中暂时还没有做,大家凑活着看下先。
c代码如下
#include <stdio.h>
#include <time.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
//#include "libswresample/swresample.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/log.h"
#include "com_example_sqqfinalrecord_FfmpegHelper.h"
#include <jni.h>
#include <android/log.h>
#define LOGE(format, ...) __android_log_print(ANDROID_LOG_ERROR, "sqqlog", format, ##__VA_ARGS__)
#define LOGI(format, ...) __android_log_print(ANDROID_LOG_INFO, "sqqlog", format, ##__VA_ARGS__)
AVBitStreamFilterContext* faacbsfc = NULL;
AVFormatContext *ofmt_ctx /*,*fmt_ctx*/;
AVCodec* pCodec,*pCodec_a;
AVCodecContext* pCodecCtx,*pCodecCtx_a;
AVStream* video_st,*audio_st;
AVPacket enc_pkt,enc_pkt_a;
AVFrame *pFrameYUV,*pFrame;
//AVAudioFifo *fifo;
//int output_frame_size;
char *filedir;
int width = 600;
int height = 800;
int framecnt = 0;
int framecnt_a = 0;
int nb_samples = 0;
int yuv_width;
int yuv_height;
int y_length;
int uv_length;
int64_t start_time;
int init_video(){
//编码器的初始化
pCodec = avcodec_find_encoder(AV_CODE