前语
其实运用原生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;
}