将RTSP流保存为本地TS文件
Posted SanShuiGeGe
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了将RTSP流保存为本地TS文件相关的知识,希望对你有一定的参考价值。
1、功能:将RTSP流保存为本地TS文件
2、存在问题:
保存mp4文件播放不了,还未解决…希望路过的大佬帮忙瞅瞅 _
3、流程:
0)初始化:并注册所有的解封装器、封装器和协议,初始化网络库;
1)打开输入的解封装上下文;
2)打开输出的封装上下文;
3)为输出的封装上下文分别新建音频流、视频流stream;
4)根据输入编码类型获取输出编码器codec;
5)为输出流的编码器参数字段codecpar设置参数(从输入编码器参数字段拷贝);
6)创建输出的编码器上下文,并通过输出编码器参数字段设置编码器上下文;
7)打开输出的编码器上下文;
8)通过输出封装上下文写文件头;
9)读取输入解封装上下文的包packet
10)根据输入的解封装上下文和输出的封装上下文的时间基准time_base,转换时间戳pts、dts,以及设置整个时间duration;
11)将音视频包以正确交织方式写入输出的封装上下文
4、代码:
/*
功能:将RTSP保存为本地ts文件
存在问题:保存mp4文件播放不了,还未解决...
*/
#include <iostream>
#include <string>
extern "C"
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
using namespace std;
AVFormatContext *inputContext = NULL;
AVFormatContext *outputContext = NULL;
// 打开输入rtsp,获得输入封装上下文
bool openInput(string& inputUrl)
// 输入的解封装上下文
int ret = avformat_open_input(
&inputContext,
inputUrl.c_str(),
0, // 0表示自动选择解封器
0 //参数设置,比如rtsp的延时时间
);
if (ret != 0)
char buf[1024] = 0 ;
av_strerror(ret, buf, sizeof(buf) - 1);
cout << "[" << __FILE__ << "|" << __LINE__ << "]" << "open " << inputUrl << " failed! :" << buf << endl;
return false;
ret = avformat_find_stream_info(inputContext, 0);
if (ret < 0)
char buf[1024] = 0 ;
av_strerror(ret, buf, sizeof(buf) - 1);
cout << "[" << __FILE__ << "|" << __LINE__ << "]" << "avformat_find_stream_info failed! " << buf << endl;
return false;
return true;
// 读取输入封装上下文的packet
AVPacket * readPacketFromSource()
AVPacket* pkt = av_packet_alloc();
int ret = av_read_frame(inputContext, pkt);
if (ret != 0)
av_packet_free(&pkt);
cout << "[" << __FILE__ << "|" << __LINE__ << "]" << "av_read_frame failed!" << endl;
return NULL;
return pkt;
// 修改时间戳
void av_packet_rescale_ts(AVPacket *pkt, AVRational src_tb, AVRational dst_tb)
if(NULL == pkt)
cout << "[" << __FILE__ << "|" << __LINE__ << "]" << "input err!" << endl;
return;
if(pkt->pts != AV_NOPTS_VALUE)
pkt->pts = av_rescale_q(pkt->pts, src_tb, dst_tb);
if(pkt->dts != AV_NOPTS_VALUE)
pkt->dts = av_rescale_q(pkt->dts, src_tb, dst_tb);
// if(pkt->pts != AV_NOPTS_VALUE)
// pkt->pts = av_rescale_q_rnd(pkt->pts, src_tb, dst_tb, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
// if(pkt->dts != AV_NOPTS_VALUE)
// pkt->dts = av_rescale_q_rnd(pkt->dts, src_tb, dst_tb, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
if(pkt->duration > 0)
pkt->duration = av_rescale_q(pkt->duration, src_tb, dst_tb);
// pkt->pts = pkt->pts < pkt->dts ? pkt->dts:pkt->pts;
pkt->pos = -1;
// 将packt写入新的封装上下文,并释放packet
bool writePacket(AVPacket *pkt)
if(NULL == pkt || NULL == inputContext || NULL == outputContext)
cout << "[" << __FILE__ << "|" << __LINE__ << "]" << "input err!" << endl;
return false;
auto inputStream = inputContext->streams[pkt->stream_index];
auto outputStream = outputContext->streams[pkt->stream_index];
av_packet_rescale_ts(pkt, inputStream->time_base, outputStream->time_base);
// 将音视频包以正确交织方式写入封装上下文
int ret = av_interleaved_write_frame(outputContext, pkt);
// 释放!
av_packet_free(&pkt);
if(ret != 0)
cout << "[" << __FILE__ << "|" << __LINE__ << "]" << "av_interleaved_write_frame failed!" << endl;
char buf[1024] = 0 ;
av_strerror(ret, buf, sizeof(buf) - 1);
cout << "avcodec_open2 failed! :" << buf << endl;
return false;
return true;
// 打开输出文件,并获取输出封装上下文
bool openOutput(string& url)
// 根据输出封装格式,创建输出上下文
int ret = avformat_alloc_output_context2(&outputContext, NULL, NULL, url.c_str());
if(ret < 0)
cout << "[" << __FILE__ << "|" << __LINE__ << "]" << "avformat_alloc_output_context2 failed!" << endl;
return false;
// 打开输出文件
ret = avio_open2(&outputContext->pb, url.c_str(), AVIO_FLAG_READ_WRITE, NULL, NULL);
if(ret < 0)
avformat_free_context(outputContext);
outputContext = NULL;
cout << "[" << __FILE__ << "|" << __LINE__ << "]" << "avio_open failed!" << endl;
return false;
unsigned int i = 0;
for(i = 0; i < inputContext->nb_streams; i++)
#if 0 // 旧版本,使用streams[i]->codec,不建议使用
AVStream *stream = avformat_new_stream(outputContext,inputContext->streams[i]->codec->codec);
ret = avcodec_copy_context(stream->codec, inputContext->streams[i]->codec);
if(ret != 0)
cout << "[" << __FILE__ << "|" << __LINE__ << "]" << "avcodec_copy_context failed!" << endl;
break;
#else // 新版本,弃用streams[i]->codec
AVCodecParameters *inCodecPara = inputContext->streams[i]->codecpar;
// 为封装上下文创建新的流
AVStream *outStream = avformat_new_stream(outputContext, avcodec_find_decoder(inCodecPara->codec_id));
if(NULL == outStream)
cout << "[" << __FILE__ << "|" << __LINE__ << "]" << "avformat_new_stream failed!" << endl;
break;
outStream->time_base.den=25;//AVRational这个结构标识一个分数,num为分数,den为分母(时间的刻度)
outStream->time_base.num=1;
// 找到和输入一样的编码器
AVCodec* outCodec = avcodec_find_decoder(inCodecPara->codec_id);
if (!outCodec)
cout << "[" << __FILE__ << "|" << __LINE__ << "]" << "can't find the codec id " << inCodecPara->codec_id << endl;
break;
// 创建输出编码器上下文
AVCodecContext *outCodecContext = avcodec_alloc_context3(outCodec);
if(NULL == outCodecContext)
cout << "[" << __FILE__ << "|" << __LINE__ << "]" << "avcodec_alloc_context3 failed!" << endl;
break;
// 输入编码器参数 拷贝到 输出编码器参数
AVCodecParameters *outCodecPara = outStream->codecpar;
ret = avcodec_parameters_copy(outCodecPara, inCodecPara);
if(ret < 0)
cout << "[" << __FILE__ << "|" << __LINE__ << "]" << "avcodec_parameters_copy failed!" << endl;
break;
// 不加这个,有的流保存为mp4会报错,[mp4 @ 031be9c0] Tag [27][0][0][0] incompatible with output codec id '27' (avc1)
// 但是加了这个,保存成Mmp4还是播放不了...
outCodecPara->codec_tag = 0;
// 输出编码器参数 拷贝到 输出编码器上下文
ret = avcodec_parameters_to_context(outCodecContext, outCodecPara);
if(ret < 0)
cout << "[" << __FILE__ << "|" << __LINE__ << "]" << "avcodec_parameters_copy failed!" << endl;
break;
// outCodecContext->bit_rate =0;//目标的码率,即采样的码率;显然,采样码率越大,视频大小越大 比特率
// outCodecContext->time_base.num=1;//下面两行:一秒钟25帧
// outCodecContext->time_base.den=15;
// outCodecContext->frame_number=1;//每包一个视频帧
// 打开编码器输出上下文
ret = avcodec_open2(outCodecContext, outCodec, 0);
if(ret != 0)
cout << "[" << __FILE__ << "|" << __LINE__ << "]" << "avcodec_open2 failed!" << endl;
break;
av_free(outCodec);
outCodec = NULL;
#endif
// 判断音视频流stream是否全部创建成功
if(i != outputContext->nb_streams)
avio_close(outputContext->pb);
avformat_free_context(outputContext);
outputContext = NULL;
cout << "[" << __FILE__ << "|" << __LINE__ << "]" << "failed!" << endl;
return false;
av_dump_format(outputContext, 0, url.c_str(), 1);//输出视频信息
// 写入文件头
ret = avformat_write_header(outputContext, NULL);
if(ret < 0)
avio_close(outputContext->pb);
avformat_free_context(outputContext);
outputContext = NULL;
cout << "[" << __FILE__ << "|" << __LINE__ << "]" << "avformat_write_header failed!" << endl;
char buf[1024] = 0 ;
av_strerror(ret, buf, sizeof(buf) - 1);
cout << "avformat_write_header failed! :" << buf << endl;
return false;
return true;
void init()
av_register_all();
avformat_network_init();
void deinit()
avformat_network_deinit();
int main()
cout << "Hello World!" << endl;
init();
string inUrl = "http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear2/prog_index.m3u8";
// inUrl = "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mp4";
int ret = openInput(inUrl);
if(true != ret)
return -1;
string outUrl = "./test1.mp4";
ret =openOutput(outUrl);
if(true != ret)
return -1;
while(1)
auto pkt = readPacketFromSource();
if(pkt)
ret = writePacket(pkt);
if(ret)
cout << "write success!" << endl;
else
cout << "write fail!" << endl;
if(outputContext)
avio_close(outputContext->pb);
avformat_free_context(outputContext);
if(inputContext)
avformat_free_context(inputContext);
deinit();
return 0;
将RTSP流保存到android中的mp4文件
我正在开发一个项目,我需要从IPCamera读取输入流。我可以通过rtsp url获取此信息。
显示IP摄像机流。我可以通过使用 -
videoView = (VideoView) this.findViewById(R.id.videoView1);
MediaController mc = new MediaController(this);
videoView.setMediaController(mc);
videoView.setVideoURI(Uri.parse("rtsp://xxxxxxxx/camera1"));
videoView.requestFocus();
现在我想将此流记录到MP4文件中。同样我使用mediarecorder.Here我被卡住了。
MediaRecorder mediaRecorder = new MediaRecorder();
//mediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
mediaRecorder.setOutputFile("rtsp://xxxxxxxxx/camera1");
try {
mediaRecorder.prepare();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mediaRecorder.start();
谢谢
答案
使用ffmpeg
。
- 添加
ffmpeg
依赖项。compile 'nl.bravobit:android-ffmpeg:1.1.5'
- 记录流。
String RTSP_URL = "rtsp://<your_rtsp_url>"; final File targetFile = new File( getExternalStoragePublicDirectory( Environment.DIRECTORY_MOVIES ) + "/recording1.mp4" ); final FFmpeg ffmpeg = FFmpeg.getInstance(this); String[] ffmpegCommand = new String[]{ "-i", RTSP_URL, "-acodec", "copy", "-vcodec", "copy", targetFile.toString() }; final FFtask ffTask = ffmpeg.execute( ffmpegCommand, new FFcommandExecuteResponseHandler() { @Override public void onStart() {} @Override public void onProgress(String message) {} @Override public void onFailure(String message) {} @Override public void onSuccess(String message) {} @Override public void onFinish() {} } ); final Timer timer = new java.util.Timer(); TimerTask timerTask = new TimerTask() { @Override public void run() { ffTask.sendQuitSignal(); } }; timer.schedule( timerTask, 30000 ); // Will stop recording after 30 seconds.
以上是关于将RTSP流保存为本地TS文件的主要内容,如果未能解决你的问题,请参考以下文章
JavaCV音视频开发宝典:rtsp转推到rtp(非TS流方式),及使用TS流发送解决sdp缺失问题