将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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值