原文链接:https://my.oschina.net/hava/blog/3127182
编者注
由于需要把FFmpeg引入Unity,则使用FFmpeg.AutoGen。独立运行demo没有问题,正常运行,但是相似的代码移入Unity 发生问题。特查询记录。
问题定位
FFmpeg.AutoGen尝试
先在顶层进行尝试,
FFmpeg 源代码分析
Generic error in an external library
由于不好定位,则直接从Exception信息开始。
error.h 57
#define AVERROR_EXTERNAL FFERRTAG( 'E','X','T',' ') ///< Generic error in an external library
libavutil/error.c
static const struct error_entry error_entries[] = {
......
{ ERROR_TAG(EXTERNAL), "Generic error in an external library" },
......
能够看到错误标签是AVERROR_EXTERNAL,但是发现有很多地方均使用该内容。则需要方法结合进行判断。
avcodec_open2
头文件:libavcodec/avcodec.h 4318
/**
* Initialize the AVCodecContext to use the given AVCodec. Prior to using this
* function the context has to be allocated with avcodec_alloc_context3().
*
* The functions avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(),
* avcodec_find_decoder() and avcodec_find_encoder() provide an easy way for
* retrieving a codec.
*
* @warning This function is not thread safe!
*
* @note Always call this function before using decoding routines (such as
* @ref avcodec_receive_frame()).
*
* @code
* avcodec_register_all();
* av_dict_set(&opts, "b", "2.5M", 0);
* codec = avcodec_find_decoder(AV_CODEC_ID_H264);
* if (!codec)
* exit(1);
*
* context = avcodec_alloc_context3(codec);
*
* if (avcodec_open2(context, codec, opts) < 0)
* exit(1);
* @endcode
*
* @param avctx The context to initialize.
* @param codec The codec to open this context for. If a non-NULL codec has been
* previously passed to avcodec_alloc_context3() or
* for this context, then this parameter MUST be either NULL or
* equal to the previously passed codec.
* @param options A dictionary filled with AVCodecContext and codec-private options.
* On return this object will be filled with options that were not found.
*
* @return zero on success, a negative value on error
* @see avcodec_alloc_context3(), avcodec_find_decoder(), avcodec_find_encoder(),
* av_dict_set(), av_opt_find().
*/
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
源代码:libavcodec/utils.c 542
int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
{
......
}
头文件翻译如下: 初始化AVCodecContext时需要AVCodec。在使用方法前,context必须使用avcodec_alloc_context3()
分配内存。
函数avcodec_find_decoder_by_name()
, avcodec_find_encoder_by_name()
,avcodec_find_decoder()
和avcodec_find_encoder()
提供搜索解码器的简单方法。
警告:该函数不是线程安全的!
注意:在解码前调用本方法(例如avcodec_receive_frame()
)
avcodec_register_all();
av_dict_set(&opts, "b", "2.5M", 0);
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!codec)
exit(1);
context = avcodec_alloc_context3(codec);
if (avcodec_open2(context, codec, opts) < 0)
exit(1);
参数:avctx需要初始化
参数:codec 访问内容的编码器。如果不为空编码器将会:
- 给avcodec_alloc_context3()提供过
- 对于context而言,这个参数要么为null要么和以前传递过编解码器相同。
参数:options 充满的AVCodecContext和私有编码选项的字典
返回:返回0为成功;错误返回负数
参考:avcodec_alloc_context3()
, avcodec_find_decoder()
, avcodec_find_encoder()
,av_dict_set()
, av_opt_find()
被遗忘的日志
虽然编写了日志注入,但是忘记了日志的初始化。则重新配置日志初始化。日志注入代码
using System;
using System.Runtime.InteropServices;
namespace FFmpeg.AutoGen.Utils
{
public class FFmpegLogging
{
public static unsafe void SetupLogging()
{
ffmpeg.av_log_set_level(ffmpeg.AV_LOG_VERBOSE);
// do not convert to local function
av_log_set_callback_callback logCallback = (p0, level, format, vl) =>
{
if (level > ffmpeg.av_log_get_level()) return;
var lineSize = 1024;
var lineBuffer = stackalloc byte[lineSize];
var printPrefix = 1;
ffmpeg.av_log_format_line(p0, level, format, vl, lineBuffer, lineSize, &printPrefix);
var line = Marshal.PtrToStringAnsi((IntPtr) lineBuffer);
if (logger != null)
{
logger(line);
}
};
ffmpeg.av_log_set_callback(logCallback);
}
public delegate void SelfLogger(string line);
public static SelfLogger logger;
}
}
日志初始化
FFmpegLogging.logger += delegate(string line) {
logger.info(line);
}
FFmpegLogging.SetupLogging();
运行结果
运行结果打印出了FFmpeg的错误内容:
[libx264 @ 00000006681c0] width not divisible by 2(1283x537)
附录
How to decode MP4 file use GPU use ffmpeg.autogen?