基于Android的手机推流
这个推流器卡了很久,基于大神的代码,但是编译的时候总是出错,ffmpeg用的版本是3.1.4,有些函数提示不用了。有些很顺利就找到了替代的函数,有些则郁闷了很久,也正因为此,才逼自己去理解其中的含义。
1.将java中获取到的字符串转换成c语言中的字符数组
strcpy(input_str,(*env)->GetStringUTFChars(env,input-jstr, NULL));
strcpy(output_str,(*env)->GetStringUTFChars(env,output-jstr, NULL));
2.初始化组件
av-register-all();
3.如果有网络,初始化
avformat-network-init();
4.打开输入函数ifmt-ctx为AVFormatContext对象,里面存储流以及参数信息
avformat-open-input(&ifmt-ctx, input-str, 0, 0)
5.将流的信息写入ifmt-ctx
avformat-find-stream-info(ifmt-ctx, 0)
6.获取输出流的AVFormatContext对象,计算出输出方式
avformat-alloc-output-context2(&ofmt-ctx, NULL, “flv”,output-str);
7.然后是流交换
需要将输入流对应的AVFormatContext中的信息写入到输出流中,AVFormatContext中有一个流数组,存储的流如音频流,视频流等等。
for (i = 0; i < ifmt_ctx->nb_streams; i++) {
AVStream *in_stream = ifmt_ctx->streams[i];
//通过id获取到对应解码器
pCodec = avcodec_find_decoder(ofmt->video_codec);
//avformat_new_stream会新建流并把新的流存储在对应streams数组中
AVStream *out_stream = avformat_new_stream(ofmt_ctx, pCodec);
if (!out_stream) {
LOGE( "Failed allocating output stream\n");
ret = AVERROR_UNKNOWN;
goto end;
}
//avcodec_copy_context,
avcodec_parameters_copy(out_stream->codecpar,in_stream->codecpar);
out_stream->codecpar->codec_tag = 0;
}
avcodec_parameters_copy,这个是最坑爹的函数,原来大神的代码是avcodec_copy_context,我在avcodec.h中找到函数说的是
If you need to transfer the stream parameters from one codec context
to another, use an intermediate AVCodecParameters instance and the
avcodec_parameters_from_context() / avcodec_parameters_to_context()
functions.*/
attribute_deprecated
int avcodec_copy_context(AVCodecContext *dest, const AVCodecContext *src);
结果完全被误导了,结果就是avformat_write_header一直返回-22,参数异常
8.打开流
avio-open2(&ofmt-ctx->pb, output-str, AVIO-FLAG-WRITE,NULL,NULL);
9.写入头文件
avformat-write-header(ofmt-ctx, NULL);
10.往外写数据
while (1) {
AVStream *in_stream, *out_stream;
//Get an AVPacket
ret = av_read_frame(ifmt_ctx, &pkt);
if (ret < 0){
LOGE( "av_read_frame_break%d---%s\n",ret,av_err2str(ret));
break;
}
//FIX:No PTS (Example: Raw H.264)
//Simple Write PTS,如果没有pts值得话需要设置,pts是显示时间戳,dts是解码是解码时间戳
if(pkt.pts==AV_NOPTS_VALUE){
//Write PTS
LOGE( "AV_NOPTS_VALUE\n");
AVRational time_base1=ifmt_ctx->streams[videoindex]->time_base;
//Duration between 2 frames (us)
int64_t calc_duration=(double)AV_TIME_BASE/av_q2d(ifmt_ctx->streams[videoindex]->r_frame_rate);
//Parameters
pkt.pts=(double)(frame_index*calc_duration)/(double)(av_q2d(time_base1)*AV_TIME_BASE);
pkt.dts=pkt.pts;
pkt.duration=(double)calc_duration/(double)(av_q2d(time_base1)*AV_TIME_BASE);
}
//Important:Delay,为了防止输出过快,加入的延时,是以视频帧为单位的
if(pkt.stream_index==videoindex){
AVRational time_base=ifmt_ctx->streams[videoindex]->time_base;
LOGE( "time_base==%d\n",pkt.stream_index);
AVRational time_base_q={1,AV_TIME_BASE};
int64_t pts_time = av_rescale_q(pkt.dts, time_base, time_base_q);
int64_t now_time = av_gettime() - start_time;
LOGE( "new_time%lld-----pts_time%lld\n",now_time,pts_time);
if (pts_time > now_time)
av_usleep(pts_time - now_time);
}
in_stream = ifmt_ctx->streams[pkt.stream_index];
out_stream = ofmt_ctx->streams[pkt.stream_index];
LOGE( "pkt.stream_index==%d\n",pkt.stream_index);
/* copy packet */
//Convert PTS/DTS
LOGE("--------pkt.pts:%lld----pkt.dts:%lld----pkt.duration:%lld\n",pkt.pts,pkt.dts,pkt.duration);
pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
pkt.pos = -1;
LOGE("pkt.pts:%lld----pkt.dts:%lld----pkt.duration:%lld\n",pkt.pts,pkt.dts,pkt.duration);
//Print to Screen
if(pkt.stream_index==videoindex){
LOGE("Send %8d video frames to output URL\n",pkt.stream_index);
frame_index++;
}
//写入数据,关键函数
ret = av_write_frame(ofmt_ctx, &pkt);
//ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
if (ret < 0) {
LOGE( "Error muxing packet%d---%s\n",ret,av_err2str(ret));
break;
}
av_packet_unref(&pkt);
}
//Write file trailer
11.写入文件尾,推流结束
av-write-trailer(ofmt-ctx);
12.最后关闭流,释放资源
avformat_close_input(&ifmt_ctx);
/* close output */
if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
avio_close(ofmt_ctx->pb);
avformat_free_context(ofmt_ctx);
if (ret < 0 && ret != AVERROR_EOF) {
LOGE( "Error occurred.\n");
return -1;
}