最近在研究NMSv3 接入WebRTC协议,以实现浏览器跨平台无插件高清推流,服务端转http-flv,rtmp等。首选视频编码当然是H264了,这样服务端只需做音频实时转码OPUS->AAC即可。

在解析RTP包内H264负载时遇到各种坑,这里记录一下。

环境:

  • OS:MacOS 10.14.6
  • Chrome : 80.0.3987.106

一、查看默认编码器参数,浏览器访问 chrome://gpu/

Video Acceleration Information

Decode h264 baseline16×16 to 4096×2160 pixels
Decode h264 extended16×16 to 4096×2160 pixels
Decode h264 main16×16 to 4096×2160 pixels
Decode h264 high16×16 to 4096×2160 pixels
Encode h264 baseline0x0 to 4096×2160 pixels, and/or 30.000 fps
Encode h264 main0x0 to 4096×2160 pixels, and/or 30.000 fps
Encode h264 high0x0 to 4096×2160 pixels, and/or 30.000 fps

此刻,RTP包的负载,I包和P包使用FU-A,SPS、PPS采用STAP-A打包。

二、通过访问chrome://flags/ ,将Hardware-accelerated video encode 选项设置为Disable,关闭硬件编码,模拟无硬件加速的环境。

继续阅读

前期曾发布过一款windows平台RTMP推送摄像头画面的浏览器插件,近期对这款产品进行了升级,现已支持捕获桌面画面进行推流。

桌面推流当然首选OBS,专业性毋庸置疑。本人开发的这款产品定位为简单业务需求,只需引导客户安装5M左右大小的插件程序,无需任何配置,即可在web网页中进行摄像头或桌面画面的捕获推流。

在本次更新中,加入了H.265编码器的支持,视频码率更低。同时,在SSE指令集加速的软编码基础上,增加了AMD/NVIDIA/INTEL独显核显硬件加速编码,CPU消耗更低。

本插件同时也加入了直播播放器的支持,可以播放rtmp,rtsp,http协议的直播流,并支持硬件加速解码,首屏秒开与延迟消除技术。继续阅读

原创文章,转载请注明: 转载自贝壳博客

本文链接地址: Windows平台浏览器内捕获桌面画面并RTMP推流

最近在研究用Emscripten开发JavaScript的直播播放器,使用ffmpeg内置的h264解码速度还是可以接受的,本想使用openh264的解码进行比较,但发现非常慢,无法达到640×480@30帧的解码速度。

本来以前也有过用openh264的项目,直接使用libopenh264进行解码速度还是很快的。但编译进ffmpeg里使用就特别慢,于是-g重新编译NodePlayer.js并开始Chrome Performance录制。
继续阅读

原创文章,转载请注明: 转载自贝壳博客

本文链接地址: ffmpeg.js使用libopenh264解码性能低的原因

此版本发布比较关注的Intel Quick Sync Video编解码器正式推出了,之前2.6的时候在master分支测试过。
相比较NVENC,之前也有测试过,就目前NV的显卡驱动,GeForce系列只支持同时两路编码会话,实在是吝啬,空有强大性能。同期测试过QSV,就没有这方面限制。
在做服务端流媒体实时转码的硬编码方面,QSV目前比较成熟。

————————————————–
下载博主编译的ffmpeg.exe:https://github.com/illuspas/ffmpeg-hw-win32

原创文章,转载请注明: 转载自贝壳博客

本文链接地址: FFmpeg 2.7发布,Intel Quick Sync Video编解码器已正式推出

最近有个海康DVR转rtmp的项目,准备用openh264试试,相比x264性能非常强,但相同码率下画质不如x264

void CALLBACK DecCBFun(long nPort,char * pBuf,long nSize,FRAME_INFO * pFrameInfo, long nUser,long nReserved2)
{
    hik_player_t *hp = (hik_player_t *)nUser;
    long lFrameType = pFrameInfo->nType;
    if (lFrameType ==T_AUDIO16)
    {
        printf("Audio nStamp:%d  size:%d pFrameInfo:%dn",pFrameInfo->nStamp,nSize,pFrameInfo->nFrameRate);

    }

    else if(lFrameType ==T_YV12)
    {
        if(hp->in_width == 0 || hp->in_height == 0) {
            hp->in_width = pFrameInfo->nWidth;
            hp->in_height = pFrameInfo->nHeight;
            avpicture_alloc(&hp->in_yuv, AV_PIX_FMT_YUV420P, hp->in_width, hp->in_height);
            avpicture_alloc(&hp->out_yuv, AV_PIX_FMT_YUV420P, hp->out_width, hp->out_height);
            WelsCreateSVCEncoder(&hp->encoder_);

            //initilize with basic parameter
            SEncParamExt param;
            memset (¶m, 0, sizeof (SEncParamBase));

            hp->encoder_->GetDefaultParams(¶m);
            param.iUsageType = CAMERA_VIDEO_REAL_TIME;
            param.fMaxFrameRate = pFrameInfo->nFrameRate;
            param.iPicWidth = hp->out_width;
            param.iPicHeight = hp->out_height;
            param.iTargetBitrate = hp->out_bitrate;
            param.iRCMode                    = RC_QUALITY_MODE;
            param.iTemporalLayerNum          = 1;
            param.iSpatialLayerNum           = 1;
            param.bEnableDenoise             = 0;
            param.bEnableBackgroundDetection = 1;
            param.bEnableAdaptiveQuant       = 1;
//            param.bEnableFrameSkip           = 0;
            param.bEnableLongTermReference   = 0;
            param.iLtrMarkPeriod             = 30;
            param.uiIntraPeriod              = (unsigned int)pFrameInfo->nFrameRate*2;
            param.eSpsPpsIdStrategy          = CONSTANT_ID;
            param.bPrefixNalAddingCtrl       = 0;
            param.sSpatialLayers[0].iVideoWidth         = param.iPicWidth;
            param.sSpatialLayers[0].iVideoHeight        = param.iPicHeight;
            param.sSpatialLayers[0].fFrameRate          = param.fMaxFrameRate;
            param.sSpatialLayers[0].iSpatialBitrate     = param.iTargetBitrate;
            param.sSpatialLayers[0].iMaxSpatialBitrate  = param.iMaxBitrate;

            hp->encoder_->InitializeExt(¶m);
//            hp->encoder_->Initialize(¶m);
            //set option, set option during encoding process
//            int g_LevelSetting = 0;
//            hp->encoder_->SetOption(ENCODER_OPTION_TRACE_LEVEL, &g_LevelSetting);
            int videoFormat = videoFormatI420;
            hp->encoder_->SetOption(ENCODER_OPTION_DATAFORMAT, &videoFormat);

            hp->file = fopen("d:\out.h264","wb+");

        }
        //printf("Video nStamp:%d size:%d width:%d height:%d fps:%d n",pFrameInfo->nStamp,nSize,pFrameInfo->nWidth,pFrameInfo->nHeight,pFrameInfo->nFrameRate);

        avpicture_fill(&hp->in_yuv,(const uint8_t*)pBuf, AV_PIX_FMT_YUV420P,hp->in_width,hp->in_height);
        libyuv::I420Scale(hp->in_yuv.data[0],hp->in_yuv.linesize[0],
                          hp->in_yuv.data[1],hp->in_yuv.linesize[1],
                          hp->in_yuv.data[2],hp->in_yuv.linesize[2],
                          hp->in_width,hp->in_height,
                          hp->out_yuv.data[0],hp->out_yuv.linesize[0],
                          hp->out_yuv.data[1],hp->out_yuv.linesize[1],
                          hp->out_yuv.data[2],hp->out_yuv.linesize[2],
                          hp->out_width,hp->out_height,libyuv::kFilterNone);

        SFrameBSInfo info;
        memset(&info, 0, sizeof (SFrameBSInfo));
        SSourcePicture pic;
        memset (&pic, 0, sizeof (SSourcePicture));
        pic.iPicWidth = hp->out_width;
        pic.iPicHeight = hp->out_height;
        pic.iColorFormat = videoFormatI420;
        pic.iStride[0] = hp->out_yuv.linesize[0];
        pic.iStride[1] = pic.iStride[2] =  hp->out_yuv.linesize[1];
        pic.pData[0] = hp->out_yuv.data[0];
        pic.pData[1] = hp->out_yuv.data[1];
        pic.pData[2] = hp->out_yuv.data[2];
        int ret = hp->encoder_->EncodeFrame(&pic, &info);
        if(info.eFrameType != videoFrameTypeSkip ) {
            if(info.eFrameType == videoFrameTypeIDR) {
           //     printf("key framen");
            }
            int first_layer = 0;
            if(hp->have_spspps) {
                first_layer = info.iLayerNum - 1;
            } else {
                hp->have_spspps = true;
            }
            for(int i=first_layer;ifile);
                    pos+=*info.sLayerInfo[i].pNalLengthInByte;
                }

            }

            //
        }


        /*
         if (encoder_) {
            encoder_->Uninitialize();
            WelsDestroySVCEncoder (encoder_);
         }
         */
    }
    else
    {

    }
}

原创文章,转载请注明: 转载自贝壳博客

本文链接地址: 一段海康SDK解码回调后缩放并用openh264编码的代码

阅读ffmpeg串流的手册FFmpeg Streaming Guide
当进行点对点串流
如果视频编码为H.264时,payload type 为 96

$ ffmpeg -re -i sample1.mp4 -an -c copy -f rtp rtp://127.0.0.1:12345

Output #0, rtp, to 'rtp://127.0.0.1:12345':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf55.48.100
    Stream #0:0(eng): Video: h264 (avc1 / 0x31637661), yuv420p, 424x240, q=2-31, 420 kb/s, 24 fps, 90k tbn, 24 tbc (default)
    Metadata:
      handler_name    : VideoHandler
Stream mapping:
  Stream #0:0 -> #0:0 (copy)
SDP:
v=0
o=- 0 0 IN IP4 127.0.0.1
s=No Name
c=IN IP4 127.0.0.1
t=0 0
a=tool:libavformat 55.48.100
m=video 12345 RTP/AVP 96
b=AS:420
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z0LAHpZiA2P8vCAAAAMAIAAABgHixck=,aMuMsg==; profile-level-id=42C01E

此时会自动输出一段SDP的代码,这时候如果直接播放继续阅读

原创文章,转载请注明: 转载自贝壳博客

本文链接地址: 使用ffmpeg进行rtp串流h.264时关于sdp的一些分析

移动设备上的H.264实时视频编码,需要考虑到cpu占用与带宽这2个限制因素,使用X264软编码,开启neon指令集优化,即使是在arm处理器下,依然可以通过优化配置达到满意的性能.
以下测试环境 一段352×288@15fps的视频,模拟摄像头采集到的数据。ipod touch4 和昨天编译出的X264:
Touch-future:~ root# ./x264 -o video_1.h264 video_1.y4m –profile baseline –preset ultrafast –fps 15

baseline
ultrafast
encoded 467 frames, 48.17 fps, 865.45 kb/s 3368054(压缩后的文件大小,单位字节)继续阅读

原创文章,转载请注明: 转载自贝壳博客

本文链接地址: Android,IOS平台上x264编码实时视频参数设置与优化

上篇使用pipe播放流后,观看的同时,如需保存成文件,使用ffmpeg也是很方便的.
既然已经是标准H264了,那就不需要再编码,直接copy流再muxer

 _______              ______________            ________
|       |            |              |          |        |
| input |  demuxer   | encoded data |  muxer   | output |
| file  | ---------> | packets      | -------> | file   |
|_______|            |______________|          |________|

代码原型请看这个这个回复,不过他的代码中,视频输出流的AVCodecContext

AVCodecContext *c;
c = o_video_stream->codec;

缺少了extradata,造成生成的MP4文件avcC没有SPS PPS信息而无法播放

添加如下代码

c->extradata = i_video_stream->codec->extradata;
c->extradata_size = i_video_stream->codec->extradata_size;

另外不需要两次打开输入流,完全是可以在播放开始后任意时间来控制开始保存文件.
使用mp4封装,在Android IOS上均可调用系统自带播放器播放

原创文章,转载请注明: 转载自贝壳博客

本文链接地址: FFmpeg取回标准H.264流后播放的同时存为MP4文件