用ffmpeg解码H264视频流

作者: 温暖C | 来源:发表于2016-09-17 14:43 被阅读7677次

    一、前言

    最近在做直播(监控类)的项目,刚开始一窍不通,各种难啊,没办法,总得做啊,于是就查资料,一步一步,最后总算是做出来了,下面就先讲一下利用ffmpeg解码H264视频流这一块。首先在iOS平台配置ffmpeg就不再详解,具体请看:
    https://cnbin.github.io/blog/2015/05/19/iospei-zhi-ffmpegkuang-jia/ 这篇博客,我在项目中使用的ffmpeg版本号是3.1,我在看上篇博客配置ffmpeg时,按步骤一步一步来,还是出现了.a文件是红色的情况,后来我就把.a文件重新导入项目中,总算是好了,真是不易啊。

    二、解码H264视频流

    1.首先创建一个文件专门用来解码,在DDH264Decoder.h文件中对外暴露以下三个方法:

    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    #import "libavcodec/avcodec.h"
    
    @interface DDH264Decoder : NSObject
    
    /* 初始化解码器 */
    - (BOOL)initH264DecoderWithWidth:(int)width height:(int)height;
    
    /* 解码视频数据并且返回图片 */
    - (void)H264decoderWithVideoData:(NSData *)VideoData completion:(void (^)(AVPicture picture))completion;
    
    /* 释放解码器 */
    - (void)releaseH264Decoder;
    
    

    2.在.m文件中实现所暴露的方法

    #import "DDH264Decoder.h"
    #import "libswscale/swscale.h"
    #include <libavformat/avformat.h>
    #import <AVFoundation/AVFoundation.h>
    
    @interface DDH264Decoder ()
    
    @property (assign, nonatomic) AVFrame *frame;
    @property (assign, nonatomic) AVCodec *codec;
    @property (assign, nonatomic) AVCodecContext *codecCtx;
    @property (assign, nonatomic) AVPacket packet;
    @property (assign, nonatomic) AVFormatContext *formatCtx;
    
    @end
    
    @implementation DDH264Decoder
    
    /**
     *  初始化视频解码器
     *
     *  @param width  宽度
     *  @param height 高度
     *
     *  @return YES:解码成功
     */
    - (BOOL)initH264DecoderWithWidth:(int)width height:(int)height {
        
        av_register_all();
        
        avformat_network_init();
        self.codec = avcodec_find_decoder(AV_CODEC_ID_H264);
        av_init_packet(&_packet);
        if (self.codec != nil) {
           self.codecCtx = avcodec_alloc_context3(self.codec);
            
            // 每包一个视频帧
            self.codecCtx->frame_number = 1;
            self.codecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
            
            // 视频的宽度和高度
            self.codecCtx->width = width;
            self.codecCtx->height = height;
            
            // 打开codec
            if (avcodec_open2(self.codecCtx, self.codec, NULL) >= 0) {
                self.frame = av_frame_alloc();
                if (self.frame == NULL) {
                    return NO;
                }
            }
        }
        
        return (BOOL)self.frame;
    }
    
    /**
     *  视频解码
     *
     *  @param data 被解码视频数据
     *
     *  @return 图片
     */
    - (void)H264decoderWithVideoData:(NSData *)VideoData completion:(void (^)(AVPicture))completion {
        @autoreleasepool {
            _packet.data = (uint8_t *)VideoData.bytes;
            _packet.size = (int)VideoData.length;
            
            int getPicture;
                avcodec_send_packet(_codecCtx, &_packet);
                getPicture = avcodec_receive_frame(self.codecCtx, self.frame);
                 av_packet_unref(&_packet);
                if (getPicture == 0) {
                    AVPicture picture;
                    avpicture_alloc(&picture, AV_PIX_FMT_RGB24, self.codecCtx->width, self.codecCtx->height);
                    
                    struct SwsContext *img_convert_ctx = sws_getContext(self.codecCtx->width,
                                                                        self.codecCtx->height,
                                                                        AV_PIX_FMT_YUV420P,
                                                                        self.codecCtx->width,
                                                                        self.codecCtx->height,
                                                                        AV_PIX_FMT_RGB24,
                                                                        SWS_FAST_BILINEAR,
                                                                        NULL,
                                                                        NULL,
                                                                        NULL);
                    // 图像处理
                    sws_scale(img_convert_ctx, (const uint8_t* const*)self.frame->data, self.frame->linesize, 0, self.codecCtx->height, picture.data, picture.linesize);
    
                    sws_freeContext(img_convert_ctx);
                    img_convert_ctx = NULL;
                    
                    if (completion) {
                        completion(picture);
                    }
                    
                    avpicture_free(&picture);
            }
        }
    }
    
    /**
     *  释放视频解码器
     */
    - (void)releaseH264Decoder {
        if(self.codecCtx) {
            avcodec_close(self.codecCtx);
            avcodec_free_context(&_codecCtx);
           self.codecCtx = NULL;
        }
        
        if(self.frame) {
            av_frame_free(&_frame);
            self.frame = NULL;
        }
        av_packet_unref(&_packet);
    }
    
    

    注意:使用完后,一定要释放,要不然会内存泄漏。

    3.在控制器中对解码后的视频数据进行处理
    我是在另外一个文件中,对数据进行了另一层加工及处理,在这里只写在控制器中对解码后的数据进行显示。

    - (void)dealAVPicture:(AVPicture)picture width:(int)width height:(int)height {
        CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
        CFDataRef refData = CFDataCreate(kCFAllocatorDefault, picture.data[0], picture.linesize[0] * height);
        CGDataProviderRef refProvider = CGDataProviderCreateWithCFData(refData);
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
       
        CGImageRef refImage = CGImageCreate(width,
                                            height,
                                            8,
                                            24,
                                            picture.linesize[0],
                                            colorSpace,
                                            bitmapInfo,
                                            refProvider,
                                            NULL,
                                            NO,
                                            kCGRenderingIntentDefault);
        UIImage *targetImage = [UIImage imageWithCGImage:refImage];
        __weak typeof(self) weakSelf = self;
        dispatch_async(dispatch_get_main_queue(), ^{
            [XSLToast hideLoadingAnimation:NO inView:weakSelf.panelImageView];
            weakSelf.panelImageView.image = targetImage;
        });
        CGColorSpaceRelease(colorSpace);
        CGImageRelease(refImage);
        CGDataProviderRelease(refProvider);
        CFRelease(refData);
    }
    
    

    相关文章

      网友评论

        本文标题:用ffmpeg解码H264视频流

        本文链接:https://www.haomeiwen.com/subject/notwettx.html