使用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

参考

RTSP协议

基于Live555实现RtspServer及高清高码率视频传输优化

Logo

更多推荐