ios下QT调用c++与object-c混合编程硬解码ffmpeg

                                                            

 第一次写博客心情好激动。。。。

       大家都知道QT跨平台,在QT下写了一个解码FFMPEG的案例,能显示,但是性能不好。用的是FFMPEG那套大家都知悉的软解码。

      ios硬解码多的是object-c写的,(我基本没有看到有c++写的),这就很坑爹了。。。因为本人工程就是QTc++!!!

然后硬着头皮看了一下各种莫名其妙符号的object-c;摸索了一下怎么在QT里面写oc的代码,没想到成功了!!!

来,上代码分析:(怎么贴图啊 ,哈哈,尴尬)首先记录一下oc编程,然后记录c++调用oc,最后记录怎么qt下实现ios硬解码

内容挺多,慢慢来看。。。

      c++与o混合编程,你需要将文件名称定义为:*.mm

 然后在.mm文件里面,写下你要的oc类:

 @interface OCClass : NSObject
-(void) receivedRawVideoFrame: (NSString *)path ;
-(void) createDecompSession;
@property (nonatomic, copy) VideoDecodeCompleteBlock completeBlock;
-(BOOL)initH264Decoder ;
- (BOOL)readStream;
@property VTDecompressionSessionRef decodeSession;
@property CMFormatDescriptionRef  formatDescription;
@property CVPixelBufferRef pixelBuffer;
@property   uint8_t *buffer;
@property   long bufferSize;
@property   long maxSize;
- (void)setupDecodeSession;
-(CVPixelBufferRef)decode;
@property uint8_t *frame_buffer;
@property long frame_size;
@property long sps_size;
@property uint8_t *pps_buffer;
@property long pps_size;
@property uint8_t *sps_buffer;
@property (nonatomic, strong) NSInputStream *inputStream;
@end
写惯c++的是看起来很别扭对吧, @interface OCClass : NSObject这个就是c++中定义一个类继承自NSObject

@property 后面定义成员变量,可以跟c++或者oc的类型都可以。。。注意!!!!坑爹的是使用,你需要加_成员变量,

例如你需要用pps_buffer这个变量,实际上应该写_pps_buffer;- (void)setupDecodeSession;-代表非静态方法;

oc里面的类对象都只能是new出来的。类定义后面需要加@end;@这个在oc很常见,例如区分c++字符串的时候;

好哒,有c功底的很容易看懂的,我就献丑啦~!!

QT c++怎么调用OC呢,当时走了很多弯路。。我试过用xcode建立.h和.m文件来定义和实现类。在qT下添加进来,然后include

这个.h文件。结果不出意外的它不认!!!搜了一下混合编程,于是便有了现在的.mm文件。

下面两个接口是.mm文件的。它的作用是.cpp文件创建对象,用对象去调用oc写的接口.

void hardDecode(char* fileUrl,void *obj)
{

     OCClass *object = (OCClass*)obj;
     NSString *path = [[NSString alloc] initWithCString:(const char*)fileUrl encoding:NSASCIIStringEncoding];
    [object receivedRawVideoFrame:path];

}
void* createObj()
{
   OCClass *obj = [[OCClass alloc]init];
   return (void*)obj;
}
在cpp里面extern一下这两个接口,杠杠的给力~!

extern void hardDecode(char* fileUrl,void *obj);
extern void* createObj();
万能的void*解决oc和c++的指针在传递过程中的转变~~
FFmpengWorker::FFmpengWorker(QObject *parent) : QObject(parent)
{
   void* m_hardobj = createObj();
}

cpp构造中就把这个occlass类对象创建出来,将对象作为参数又传到接口里,就可以调用oc的接口啦~!!酸爽是不是!

 std::string str = fileUrl.toStdString();
        char* url = (char*)str.c_str();
        hardDecode(url,m_hardobj);

这样就转到.mm文件里面去调用hardDecode这个接口了,上面对这个接口实现,void*可以转换回来occlass*了(感谢void*

),补充一下oc函数调用,怕你们看的难受。。 [[OCClass alloc]init];这个就是调用alloc接口并初始化;[]你可以认为是函数

调用,传参也好奇怪的说。。 [object receivedRawVideoFrame:path];就是调用receivedRawVideoFrame接口并

传入path的参数,传参需要用:;第一个参数不需要说明,第二三以后的还需要加一个类似说明的标志。。。坑爹吧。。。

看:  NSString *path = [[NSString alloc] initWithCString:(const char*)fileUrl encoding:NSASCIIStringEncoding];

这个fileUrl是第一个参数,NSASCIIStringEncoding为第二个参数,前面是不是加了个encoding!这个把char*

转换成NSString*;

上面有函数的原型,实-(void) receivedRawVideoFrame: (NSString *)path{}

这个里面可以发挥你天马行空的c++思维,c++和oc都可以在这里面编译过。

来堆一堆{}里面的代码

  self.inputStream = [NSInputStream inputStreamWithFileAtPath:path];
        [self.inputStream open];
       _bufferSize = 0;
       _maxSize = 10000*1000;
       _buffer =(uint8_t*)malloc(_maxSize);
    while(true)
    {
        if([self readStream]==NO)
        {
             NSLog(@"播放结束");
             break;
        }
        uint32_t nalSize = (uint32_t)(_frame_size - 4);
        uint32_t *pNalSize = (uint32_t *)_frame_buffer;
        *pNalSize = CFSwapInt32HostToBig(nalSize);

               //NAL的类型(startCode后的第一个字节的后5位)
        int NAL_type = _frame_buffer[4] & 0x1f;

        switch (NAL_type) {
                   case 0x5:
                       NSLog(@"Nal type is IDR frame");
                       if (!_decodeSession){
                           [self setupDecodeSession];
                       }
                       _pixelBuffer = [self decode];
                       break;
                   case 0x7:
                       NSLog(@"Nal type is SPS");
                       //从帧中获取sps信息
                       _sps_size = _frame_size-4;
                       if (!_sps_buffer){
                           _sps_buffer = (uint8_t*)malloc(_sps_size);
                       }
                       memcpy(_sps_buffer, _frame_buffer+4, _sps_size);
                       break;
                   case 0x8:
                       NSLog(@"Nal type is PPS");
                       //从帧中获取sps信息
                       _pps_size = _frame_size-4;
                       if (!_pps_buffer){
                           _pps_buffer = (uint8_t*)malloc(_pps_size);
                       }
                       memcpy(_pps_buffer, _frame_buffer+4, _pps_size);
                       break;
                   default:
                       //图像信息
                       NSLog(@"Nal type is B/P frame or another");
                       _pixelBuffer = [self decode];
                       break;
               }

    }

酸爽的有木有!

我不知道专写oc的怎么看的,反正我这个c++是硬着头皮看的:

大体的意思是打开这个文件,读数据 ;

- (BOOL)readStream{

    if (_bufferSize<_maxSize && self.inputStream.hasBytesAvailable) {
        //正数:读取的字节数,0:读取到尾部,-1:读取错误
        NSInteger readSize = [self.inputStream read:_buffer+_bufferSize maxLength:_maxSize-_bufferSize];
        _bufferSize += readSize;
    }
    //对比buffer的前四位是否是startCode(每一帧前都有startCode),并且数据长度需要大于startCode
    if (memcmp(_buffer, startCode, 4) == 0 && _bufferSize > 4){
        //buffer的起始和结束位置
        uint8_t *startPoint = _buffer + 4;
        uint8_t *endPoint = _buffer + _bufferSize;
        int i=0;
        while (startPoint != endPoint) {
              i++;
            //获取当前帧长度(通过获取到下一个0x00000001,来确定)
            if (memcmp(startPoint, startCode, 4) == 0){
                //找到下一帧,计算帧长
                _frame_size = startPoint - _buffer;
                //置空帧
                if (_frame_buffer){
                    free(_frame_buffer);
                    _frame_buffer = NULL;
                }
                _frame_buffer = (uint8_t *)malloc(_frame_size);
                //从缓冲区内复制当前帧长度的信息赋值给帧
                memcpy(_frame_buffer, _buffer, _frame_size);
                uint8_t *tt=_frame_buffer;
                uint8_t* ttt = _buffer;
                uint8_t siz = _frame_size;
                //缓冲区中数据去掉帧数据(长度减少,地址移动)
                memmove(_buffer, _buffer+_frame_size, _bufferSize-_frame_size);
                uint8_t* tttt = _buffer;
                _bufferSize -= _frame_size;
                uint32_t sizt= _bufferSize;
                return YES;
            }else{
                //如果不是,移动指针
                startPoint++;
            }
        }
        //置空帧

    }
    return NO;
}

好吧有木有必要分析一下怎么解析读进来的例如.h264的数据。。。

  ....00000001 *7........00000001 *8..........00000001*6.........00000001*5........

一定要耐着性子看:读进来的数据就是上面一堆二进制,上面的readstream教你怎么把这个数据截取

7,8,6,5,1就是你要的爱。。。你会从中得到sps,pps,时间,等等你想要的,因为这些你可以调用oc里面的接口可以得到

你想要的包含有yuv的 CVPixelBufferRef 数据啊 !!瞬间激动有木有!

这里经历了好多坑~,重点在 CFDictionaryRef怎么填写,会直接决定你解出来的CVPixelBufferRef存放yuv的结构,

网上好多都是直接调用苹果的自己的player接口可以播放,只要你得到CMBlockBufferRef数据,按照它的参数往里面传。

但是我想直接要他的yuv呢!!!时间不多,先吃饭,再继续

补充一下硬解码你需要的qt Framework怎么写哈:

在.pro文件里面

 QMAKE_LFLAGS += -framework VideoToolbox
    QMAKE_LFLAGS += -framework CoreMedia
    QMAKE_LFLAGS += -framework CoreVideo
    QMAKE_LFLAGS += -framework CoreFoundation

搞几句这么暴力的代码。

感谢一颗成长道路的向日葵Qing。

你可能感兴趣的:(初次博客,硬解码,ios,qt,ffmpeg)