FFMPEG通过管道将图片推送流媒体

         最近遇到个需求,将私有协议的码流,就是比较老的视频设备啦,新设备都支持标准H264,H265了,或者私有平台协议的视频,将这些私有协议视频通过转码推送到标准的流媒体服务器,然后通过网页不使用插件进行。目前无插件播放的播放器很多比如video.js啦,一般只支持H264的flv格式,HLS等,当然也有支持H265的,不过这些都不是重点,今天的重点是怎么通过FFMPEG将私有协议的码流转化为标准的H264然后通过FFMPEG推送到流媒体服务器,从而通过网页直接播放。

        这个需求嘛,做的方式也很多,比如先对接私有SDK,然后通过私有解码库解码为YVU然后在编码转发,这是一个比较普遍的做法,也很常规。不过其实比较麻烦, 需要对编解码有一定基础。下面我介绍的方法较为简单,也很通用,只需要私有SDK支持抓图功能即可,一般私有SDK抓图的接口基本都有。

      主要思路就是使用私有SDK的抓图接口,不停的抓图,然后将图片数据输送到FFMPEG的管道,这样就可以源源不断的推送了。

     首先是FFMPEG的管道命令使用方法 

      ffmpeg.exe -f image2pipe -i \\.\\pipe\MyPipe -pix_fmt yuv420p -vcodec libx264 -f rtsp -rtsp_transport tcp "rtsp://127.0.0.1:8554/1234/1"

     调用这个命令后,FFMPEG就会连接\\.\\pipe\MyPipe的命名管道,如果没有打开命名管道,FFMPEG会退出,如果有,则会等待管道数据,我们就是利用此功能进行推送

     好了,废话不多说,上干货,也就是代码,这里以windows为例,linux大同小异就是管道的使用方法不同而已,大家学会举一反三

 

long StartProgress()//调用FFMPEG命令推流
{
    char sProcessID[32] = {0};
    sprintf_s(sProcessID,sizeof(sProcessID),"%d",GetCurrentProcessId());

    char sparam[1024];
    //调用FFMPEG命令通过管道数据推流,这里想推什么流改后面的推流地址即可,FFMPEG很强大的
    sprintf(sparam,"ffmpeg.exe -f image2pipe -i \\\\.\\pipe\\MyPipe -pix_fmt yuv420p -vcodec libx264 -f rtsp -rtsp_transport tcp \"rtsp://127.0.0.1:8554/1234/1\"");

    PROCESS_INFORMATION pi;
    STARTUPINFO    si;
    memset(&si, 0, sizeof(si));
    si.cb = sizeof(STARTUPINFO);
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.lpTitle = "";
    si.wShowWindow = SW_SHOWNORMAL;

    DWORD dwExitCode =0;
    BOOL ret = ::CreateProcess(NULL,(char*)sparam, NULL, NULL, FALSE,CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
    if (ret)
    {
        
        //m_lpid = pi.dwProcessId;

        //GF_AddProcessToJobObject(pi.hProcess);
        //关闭子进程的主线程句柄
        
        //等待子进程的退出
        //WaitForSingleObject(pi.hProcess,INFINITE);
        //获取子进程的退出码
        //GetExitCodeProcess(pi.hProcess, &dwExitCode);

        //::CloseHandle((HANDLE)pi.dwProcessId); 
        ::CloseHandle((HANDLE)pi.hThread);
        //关闭子进程句柄
        //CloseHandle(pi.hProcess);
    }else
    {
        //LOGE << "idx:" << m_lidx << " Start Progress Failed.error:" << GetLastError();
    }
    return 0;

}

    

int cappic()//抓图函数
{
    CString str;
    CString strDIR;
    int num = 1;
    int nDir = 0;

    SECURITY_ATTRIBUTES sa;
    SECURITY_DESCRIPTOR sd;
    if( InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) )
    {
        // add a NULL disc. ACL to the security descriptor.
        if (SetSecurityDescriptorDacl(&sd, TRUE, (PACL) NULL, FALSE))
        {
            sa.nLength = sizeof(sa);
            sa.lpSecurityDescriptor =&sd;
            sa.bInheritHandle = TRUE;
            //创建一个命名管道,在windows中\代表zhuan'yi两个\\代表一个\ 
            HANDLE hNamedPipe = CreateNamedPipeA("\\\\.\\pipe\\MyPipe", 
                PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 
                PIPE_TYPE_BYTE, 1, 1024, 1024,0 , &sa); 
            //检查是否创建成功 
            if (hNamedPipe == INVALID_HANDLE_VALUE) 
            { 
                printf("create named pipe failed!\n"); 
            } 
            else 
            { 
                printf("create named pipe success!\n"); 
            } 

            //异步IO结构 
            OVERLAPPED op; 
            ZeroMemory(&op, sizeof(OVERLAPPED)); 
            //创建一个事件内核对象 
            op.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 
            //等待一个客户端进行连接 
            BOOL b = ConnectNamedPipe(hNamedPipe, &op); 
            //开启FFMPEG,并调用命令
            StartProgress();
            //当有客户端进行连接时,事件变成有信号的状态 
            if (WaitForSingleObject(op.hEvent, INFINITE) == 0) 
            { 
                printf("client connect success!\n"); 
            } 
            else 
            { 
                printf("client connect failed!\n"); 
            } 

            unsigned char*  buff = new unsigned char[1024*1024*100]; 
            memset(buff,0,1024*1024*100);
            CFile file;
            
            while(TRUE)
            {
                if(m_lPlayBackHandle == -1)
                    return -1;

                //下面的函数就是私有SDK抓图函数,自己改为自己的抓图接口即可

                str.Format("G:\\test\\conver\\bin\\1234\\1.bmp",num);
                GF_CreateDir(str);
                int nRes = EX_NET_CapturePicture(0,m_lPlayBackHandle,(char*)(LPCTSTR)str);

                //--------抓图接口抓图完成后,读取图像数据,通过管道输入到FFMPEG
                if(file.Open(str,CFile::modeReadWrite))
                {    
                    memset(buff,0,1024*1024*100);
                    int nLen = file.Read(buff,1024*1024*100);
                    DWORD cbWrite; 
                    WriteFile(hNamedPipe, buff, nLen, &cbWrite, NULL); 
                    file.Close();
                }
                Sleep(10);
            }
        }
    }
    return 0xFFFF;
}

 

效果图上个

说明推流成功啦

VLC播放效果

CPU占用略高,FFMPEG转码占用CPU本身比较高

我I5的CPU大概解码15%,编码15%左右

此文仅用来抛砖引玉,给出个思路,具体应用到项目中,还需很多工作

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值