ios ffmpeg 实时视频压缩(主要是H264)

在xcode上实现 iphone 实时传输当前画面的功能,本文件针对 h264 编码,如果需要其他编码 ,可以把video_encode_frame_init()里面的 编码id替换掉


1、 ffmpeg 视频流文件

使用之前编译好ffmpeg 库,并且加载了x264的库,然后将下面代码拷贝到一个空白的源文件中,它再次封装了视频流处理的四个流程。

一、 注册编码器

void video_encode_register();

注册所有编码(当然前提是你的ffmpeg库加载了所有的编码器),放在程序启动时执行,只需执行一次。


二、初始化

void video_encode_init();

初始化参数。

三、配置参数

void video_encode_frame_init(int gop_size,int bitrate,int frames_per_second_,int width,int height);

配置编码参数。需要在抓屏开始之前设置

关键参数:

bit_rate:比特率,即每秒种播放视频的比特数。

keyint_min:最小关键帧(I-frame)间隔

gop_size:关键帧间隔,也是最大间隔

max_b_frames:最大B-frame个数,与gop_size配合使用。等于0表示无B-frame

关键帧即key frame,也就是处于关键地位的。比如关键帧间隔是5,第1帧是关键帧,则第6帧也是关键帧,那么第1、6帧如果单独取出来保存就是一张完整的图片,而从第2~5帧只是存储了与第1帧的差别,这样减小了帧总体数据的大小。一般来说 关键帧越大,最终视频的数据越小。但太大了很可能导致中间出现很多失真的画面。

pix_fmt =AV_PIX_FMT_YUV420P; 帧格式,这里只能是这个,因为h264格式只能出来 yuv420p 的图片,所以如果RGBA的图片,需要进行转换。

width,height:帧的长宽。

time_base:即每秒播放多少帧。


四、进行编码

<pre name="code" class="cpp">int  video_encode_process(AVPacket  *pkt,const uint8_t* imgbuf,int cw,int ch);

 

每隔一段时间抓屏一次,并将获取的图片数据保存,应该是bmp 位图,其他格式图片存在文件头,矢量图数据保存之后不是视频

这时候cw,ch指的是图片实际的长宽,而不是帧的长宽,但是最终会转化成帧的长宽。不建议图片实际长宽与帧长宽不一致,因为那样图片会失真。建议如果图片长宽与实际长宽不一致,可以先转换成长宽一致的图片。比如用Qt里的 scaleTo()等方法,而不是 用ffmpeg的方法。

例如:

AVPacket pkt;
char *imgbuf=captureScreen();//抓屏函数,返回的是image 的首地址,这里不存在这个API,需要自己根据平台自己完成
if(video_encode_process(&pkt,imgbuf,320,480)>0)
{
   save_file(pkt.data,pkt.len);//保存数据到一个文件中
   av_pkt_free(&pkt);//释放pkt,因为data被申请了内存
}

五、编码结束处理

video_encode_end(AVPacket *pkt);

//
//  h264video.c
//  AppLinkTester2
//
//  Created by on 15-5-20.
//
//

#include <stdio.h>
#include "h264video.h"


AVFrame *pframe;
AVCodecContext *contex;
FILE  *fhandle;
int frames_per_second;

void video_encode_register()
{
    av_register_all();
    avcodec_register_all();

    contex=NULL;
    pframe=NULL;
}

//31ms
void rgb32_to_yuv420(const uint8_t *rgb, AVFrame* frame,int cw,int ch)
{
    printf("rgb32_to_yuv420 start:\n");
    const uint8_t *rgb_src[3]= {rgb, NULL, NULL};
    int rgb_stride[3]={4*cw, 0, 0};
    
    struct SwsContext *yuvContext=sws_getContext(cw,ch,AV_PIX_FMT_RGBA,frame->width,frame->height,AV_PIX_FMT_YUV420P,SWS_BILINEAR, NULL, NULL, NULL);
    
    int oh=sws_scale(yuvContext,rgb_src,rgb_stride, 0, ch, frame->data, frame->linesize);
    if (oh!=frame->height) {
        printf("rgb32 to yuv420 error\n");
    }
    sws_freeContext(yuvContext);
    printf("rgb32_to_yuv420 end:\n");
}

//130ms  1
void video_encode_init()
{
    printf("video_encode_init start:\n");

        if (contex){
            avcodec_close(contex);
            av_free(contex);
        }
    if (pframe) {
        av_free(pframe);
    }
    contex=NULL;
    pframe=NULL;
    
    printf("video_encode_init end:\n");
}


int video_frame_get_width()
{
    return pframe->width;
}

int video_frame_get_height()
{
    return pframe->height;
}

//gop_size 关键幀间隔   biterate 比特率  单位:kb/s
void video_encode_frame_init(int gop_size,int bitrate,int frames_per_second_,int width,int height)
{
    AVCodec *codec=NULL;
    frames_per_second=frames_per_second_;
    
    codec=avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!codec) {
        fprintf(stderr, "Codec not found\n");
        return;
    }
    
    contex = avcodec_alloc_context3(codec);
    if (!contex) {
        fprintf(stderr, "Could not allocate video codec context\n");
        return;
    }
    
    contex->bit_rate = bitrate*1000;
    contex->keyint_min=0;
//    contex->scenechange_threshold
    contex->time_base=(AVRational){1,frames_per_second};
    contex->gop_size =gop_size;
    contex->max_b_frames = 0;
    contex->delay=0;
    contex->pix_fmt =AV_PIX_FMT_YUV420P;//
    
    av_opt_set(contex->priv_data, "preset", "superfast", 0);//superfast
    av_opt_set(contex->priv_data,"tune","zerolatency",0);
    
    contex->width = ((int)(width/2))*2;
    contex->height = ((int)(height/2))*2;
        
    if(avcodec_open2(contex, codec, NULL)){
        fprintf(stderr, "Could not open codec\n");
    }
    
    pframe = av_frame_alloc();
    if (!pframe) {
        fprintf(stderr, "Could not allocate video frame\n");
        return;
    }
//    pframe->pict_type=AV_PICTURE_TYPE_NONE;
//    pframe->key_frame=0;
    pframe->format=contex->pix_fmt;//
    pframe->width=contex->width;
    pframe->height=contex->height;
    
    int ret=av_image_alloc(pframe->data, pframe->linesize, contex->width, contex->height, contex->pix_fmt, 32);
    if(ret < 0){
        fprintf(stderr, "Couldn't alloc raw picture buffer\n");
        return;
    }
    pframe->pts=0;
}



int  video_encode_end(AVPacket *pkt)
{
//    uint8_t endcode[]={0,0,1,0xb7};
    //    FILE *f=fopen("/Users/patrick/git/png_to_video_h264/test01/test03.264", "wb+");

    av_init_packet(pkt);
    pkt->data = NULL;    // packet data will be allocated by the encoder
    pkt->size = 0;
    
    /* get the delayed frames */
    
    for (int got_output = 1; got_output; ) {
        fflush(stdout);
        
        int ret = avcodec_encode_video2(contex, pkt, NULL, &got_output);
        if (ret < 0) {
            fprintf(stderr, "Error encoding frame\n");
            return ret;
        }
        
        if (got_output) {
            pframe->pts++;
            printf("Write frame (size=%5d)\n", pkt->size);
//            fwrite(pkt.data, 1, pkt.size, fhandle);
            //            [self sendPacket:pkt.data  len:pkt.size];
//            av_free_packet(&pkt);
            
        }
    }
    
    
//    fwrite(endcode, 1, sizeof(endcode), fhandle);
    avcodec_close(contex);
    //
    //    [self sendPacket:endcode  len:sizeof(endcode)];
    printf("编码完成,共%d张%d幀\n",(int)pframe->pts,(int)pframe->pts);
    printf("视频时间应该是%f秒\n",(1.0*pframe->pts/frames_per_second));
    return pkt->size;
    
}

//6ms~ 300ms
int  video_encode_process(AVPacket  *pkt,const uint8_t* imgbuf,int cw,int ch)
{
    printf("video_encode_process start:\n");
    
    av_init_packet(pkt);
    pkt->data = NULL;    // packet data will be allocated by the encoder
    pkt->size = 0;
    
    int got_output=-1;
    if(contex == NULL||pframe == NULL){
        printf("encode contex is null\n");
        return -1;
    }
    if(contex->codec_id==AV_CODEC_ID_NONE){
        printf("contex is invalid");
        return -1;
    }
    
    rgb32_to_yuv420(imgbuf,pframe,cw,ch);
    
    fflush(stdout);
    
    /* encode the image */
    int ret = avcodec_encode_video2(contex, pkt, pframe, &got_output);
    
    if (ret < 0) {
        fprintf(stderr, "Error encoding frame\n");
        return ret;
    }
    
    if (got_output) {
        fflush(stdout);
        pframe->pts++;
        printf("Write frame %3d (size=%5d)\n", (int)pframe->pts, pkt->size);
        if (pkt->flags&AV_PKT_FLAG_KEY) {
            printf("key frame packet %d,pts=%ld",pframe->pts,pkt->pts);
        }
    }
    
    //free(&pFrame->data[0]);
    printf("video_encode_process end:\n");
    return pkt->size;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值