前言
其实使用原生FFmpeg的软解和硬解很简单,硬解以iOS为例只需要编译的库里面含有videotoolbox模块。 注意:如果是使用ijk编译的库是直接调用硬解是不会成功的
软解流程
1.注册所有解码器
avcodec_register_all();
2.找到h264编解码器信息
AVCodec * codec_dec = avcodec_find_decoder(AV_CODEC_ID_H264);
3.通过编解码器信息创建上下文
AVCodecContext * c_dec = avcodec_alloc_context3(codec_dec);
4.通过配置参数初始化具体的解码器(第三个参数为AVDictionary可以配置解码器的具体参数)
if (avcodec_open2(c_dec, codec_dec,NULL) < 0)
{
av_free(c_dec);
return NULL;
}
5.初始化AVFrame
用于接收解码后的数据
AVFrame * pic_tmp = av_frame_alloc();
if(pic_tmp == NULL)
{
avcodec_close(c_dec);
av_free(c_dec);
return ;
}
6.Socket收到数据包后在CADisplayLink中调用解码
FramePacket
:是自定义的传输数据包- XYQCodecCtx:就是
AVCodecContext
- XYQFrame:是
AVFrame
- 最后使用
YUV420p
渲染出来 _pframeList
是缓存数据列表
- (UIImage *)decodeAndDisplay:(FramePacket *)fp{
AVPacket packet;
av_new_packet(&packet, fp->len);
memcpy(packet.data, fp->data, fp->len);
int ret=0;
ret = avcodec_send_packet(XYQCodecCtx, &packet);
if (ret != 0) {
NSLog(@"----6");
av_packet_unref(&packet);
[self->_pframeList DPListPopFront];
return nil;
}
ret = avcodec_receive_frame(XYQCodecCtx, XYQFrame);
if (ret == 0){
NSLog(@"----7");
dispatch_async(dispatch_get_main_queue(), ^{
[self aVFrameToYUV420pToDisplay:self->XYQFrame];
});
}
av_packet_unref(&packet);
[self->_pframeList DPListPopFront];
return nil;
}
硬解流程
和软解流程差不多只需要在第四步avcodec_open2
前增加硬解的参数配置
1.找到videotoolbox硬解
const char *codecName = av_hwdevice_get_type_name(AV_HWDEVICE_TYPE_VIDEOTOOLBOX);
enum AVHWDeviceType type = av_hwdevice_find_type_by_name(codecName);
if (type != AV_HWDEVICE_TYPE_VIDEOTOOLBOX) {
return NULL;
}
2.初始化硬解,将硬解加入到上下文中
AVBufferRef *hw_device_ctx = NULL;
static int InitHardwareDecoder(AVCodecContext *ctx, const enum AVHWDeviceType type) {
int err = av_hwdevice_ctx_create(&hw_device_ctx, type, NULL, NULL, 0);
if (err < 0) {
log4cplus_error("XDXParseParse", "Failed to create specified HW device.\n");
return err;
}
ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);
return err;
}
最后有一点需要注意,在硬解中我们是直接从AVFrame
能拿到CVPixelBufferRef
数据的,他是在data的第四个数据,而在软解中使用的是前三个数据
CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)videoFrame->data[3];
拿到之后就能使用opengl
渲染了,这里就不多赘述了
扩展
前面是已知h264
的情况下进行的解码,如果是未知的情况下可以用过读取流信息来赋予他解码信息
- 配置解码信息
- formatContext:AVFormatContext
int ret = av_find_best_stream(formatContext, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);
if (ret < 0) {
log4cplus_error(kModuleName, "av_find_best_stream faliture");
return NULL;
}
2.配置解码参数
ret = avcodec_parameters_to_context(codecContext, formatContext->streams[videoStreamIndex]->codecpar);
if (ret < 0){
log4cplus_error(kModuleName, "avcodec_parameters_to_context faliture");
return NULL;
}
打开文件流的代码(创建AVFormatContext)
- (AVFormatContext *)createFormatContextbyFilePath:(NSString *)filePath {
if (filePath == nil) {
log4cplus_error(kModuleName, "%s: file path is NULL",__func__);
return NULL;
}
AVFormatContext *formatContext = NULL;
AVDictionary *opts = NULL;
av_dict_set(&opts, "timeout", "1000000", 0);//设置超时1秒
formatContext = avformat_alloc_context();
BOOL isSuccess = avformat_open_input(&formatContext, [filePath cStringUsingEncoding:NSUTF8StringEncoding], NULL, &opts) < 0 ? NO : YES;
av_dict_free(&opts);
if (!isSuccess) {
if (formatContext) {
avformat_free_context(formatContext);
}
return NULL;
}
if (avformat_find_stream_info(formatContext, NULL) < 0) {
avformat_close_input(&formatContext);
return NULL;
}
return formatContext;
}
★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。
见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓