使用rtsp拉取rtsp服务器的视频,为了方便测试,在本地linux虚拟机中搭建了一个rtsp服务器(参考搭建rtsp服务器),rtsp拉流端通过ffmpeg实现(rtsp拉流和rtmp拉流类似).
代码
/**
laliu1: 将rtsp流保存到本地视频文件
usage:
./a.out rtsp://localhost/rango.ts xx.ts 将rtsp流保存为本地文件
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libavdevice/avdevice.h"
#include "libswscale/swscale.h"
static int64_t s_time = 0;
void set_block_timeout_time(int64_t time)
{
s_time = time;
}
int64_t get_systime()
{
int64_t time;
static int64_t start_time = 0;
int64_t now_time;
if(start_time == 0){
start_time = av_gettime();
return 0;
}
now_time = av_gettime();
time = now_time - start_time;
return time;
}
static int interrupt_cb(void * arg)
{
if( get_systime() > s_time){
printf("time out:%lld,%lld\n", s_time, get_systime());
return 1;
}
return 0;
}
int main(int argc, char * argv[])
{
AVFormatContext *pInFmtContext = NULL;
AVStream *in_stream;
AVCodecContext *pInCodecCtx;
AVCodec *pInCodec;
AVPacket *in_packet;
AVFormatContext * pOutFmtContext;
AVOutputFormat *outputFmt;
AVStream * out_stream;
//AVCodecContext * pOutCodecCtx;
//AVCodec *pOutCodec;
//AVPacket *out_packet;
//AVFrame *pOutFrame;
AVRational frame_rate;
double duration;
//int picture_size = 0;
//FILE *fp;
int ret;
const char * default_url = "rtsp://localhost:80/rango.ts";
char in_file[128] = {0};
char out_file[256] = {0};
int videoindex = -1;
int audioindex = -1;
int video_frame_count = 0;
int audio_frame_count = 0;
int video_frame_size = 0;
int audio_frame_size = 0;
int i;
int got_picture;
av_log_set_level(AV_LOG_DEBUG);
if(argc < 2){
printf("Usage: a.out <in_file> <out_file>\n");
return -1;
}
strcpy(in_file, argv[1]);
strcpy(out_file, argv[2]);
//av_register_all();
//avformat_network_init();
// input ....................
pInFmtContext = avformat_alloc_context();
pInFmtContext->interrupt_callback.callback = interrupt_cb;
pInFmtContext->interrupt_callback.opaque = pInFmtContext;
set_block_timeout_time(get_systime() + 10000000);
// Open an input stream and read the header,
if( (ret = avformat_open_input ( &pInFmtContext, in_file, NULL, NULL)) < 0){
printf("avformat_open_input failed:%d\n",ret);
return -1;
}
//查询输入流中的所有流信息
if( avformat_find_stream_info(pInFmtContext, NULL) < 0){
printf("avformat_find_stream_info failed\n");
return -1;
}
//print
av_dump_format(pInFmtContext, 0, in_file, 0);
// output ..............................
ret = avformat_alloc_output_context2(&pOutFmtContext, NULL, NULL, out_file);
if(ret < 0){
printf("avformat_alloc_output_context2 filed:%d\n",ret);
return -1;
}
for(i=0; i < pInFmtContext->nb_streams; i++){
in_stream = pInFmtContext->streams[i];
if(in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
videoindex = i;
}
if(in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){
audioindex = i;
}
pInCodec = avcodec_find_decoder(in_stream->codecpar->codec_id);
out_stream = avformat_new_stream(pOutFmtContext, pInCodec);
ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
if(ret < 0){
printf("avcodec_parameters_copy failed\n");
return -1;
}
out_stream->codecpar->codec_tag = 0;
if( pOutFmtContext->oformat->flags & AVFMT_GLOBALHEADER){
out_stream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
}
}
av_dump_format(pOutFmtContext, NULL, out_file, 1);
ret = avio_open(&pOutFmtContext->pb, out_file, AVIO_FLAG_WRITE);
if(ret < 0){
printf("avio_open failed\n");
return -1;
}
ret = avformat_write_header(pOutFmtContext, NULL);
if( ret < 0){
printf("avformat_write_header failed\n");
return -1;
}
in_packet = av_packet_alloc();
int frame_index = 0;
while(1){
set_block_timeout_time(get_systime() + 5000000);
in_packet->pts = 0;
ret = av_read_frame(pInFmtContext, in_packet);
if(ret < 0)
break;
in_stream = pInFmtContext->streams[in_packet->stream_index];
out_stream = pOutFmtContext->streams[in_packet->stream_index];
if(in_packet->pts == AV_NOPTS_VALUE){
printf("AV_NOPTS_VALUE\n");
AVRational time_base1 = pOutFmtContext->streams[videoindex]->time_base;
int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(pOutFmtContext->streams[videoindex]->r_frame_rate);
in_packet->pts = (double)(frame_index * calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE);
in_packet->dts = in_packet->pts;
in_packet->duration = (double)calc_duration / (double)(av_q2d(time_base1)*AV_TIME_BASE);
in_packet->pos = -1;
frame_index++;
}
av_packet_rescale_ts(in_packet, in_stream->time_base, out_stream->time_base);
if(in_packet->stream_index == videoindex){
video_frame_size += in_packet->size;
printf("recv %5d video frame %5d-%5d\n", ++video_frame_count, in_packet->size, video_frame_size);
}
if(in_packet->stream_index == audioindex){
audio_frame_size += in_packet->size;
printf("recv %5d audio frame %5d-%5d\n", ++audio_frame_count, in_packet->size, audio_frame_size);
}
ret = av_interleaved_write_frame(pOutFmtContext, in_packet);
if(ret < 0){
printf("av_interleaved_write_frame failed\n");
break;
}
av_packet_unref(in_packet);
}
av_write_trailer(pOutFmtContext);
av_packet_free(&in_packet);
avformat_close_input(&pInFmtContext);
avio_close( pOutFmtContext->pb);
avformat_free_context(pOutFmtContext);
return 0;
}
编译
gcc laliu.c -lavformat -lavcodec -lavutil
验证
1. 启动rtsp服务器
./mediaServer/live555MediaServer
2. 启动拉流
./laliu rtsp://localhost:80/go.ts xx.ts
接下来,就能看到拉流器生成的视频文件xx.ts