ffmpeg
日志
ffmpeg
日志,接口都定义在log.h
,使用的库是libavutil
使用时一定要加上对头文件extern "C"
外部,否则会报对应的函数没有定义
//
// Created by andrew on 2020/10/31.
//
#include <iostream>
extern "C"{
#include <libavutil/log.h>
}
using namespace std;
int main(int argc, char *argv[])
{
av_log_set_level(AV_LOG_DEBUG);
av_log(NULL, AV_LOG_DEBUG,"Hello world!\n");
return 0;
}
执行结果:
Hello world!
write file
第一步先设置日志等级,这里追溯下日志等级设置的实现。
av_log_set_level
设置日志等级
前期追溯过的函数后面将不会进行重复追踪定位。
av_log_set_level(AV_LOG_DEBUG);
函数点击进入函数,函数实现如下,只是设置了一个全局变量,用于记录日志的等级
void av_log_set_level(int level)
{
av_log_level = level;
}
再看下实际使用时,函数是如何判定日志是否需要输出的
void av_log(void* avcl, int level, const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
av_vlog(avcl, level, fmt, vl);
va_end(vl);
}
//实际调用
void av_vlog(void* avcl, int level, const char *fmt, va_list vl)
{
AVClass* avc = avcl ? *(AVClass **) avcl : NULL;
void (*log_callback)(void*, int, const char*, va_list) = av_log_callback;
if (avc && avc->version >= (50 << 16 | 15 << 8 | 2) &&
avc->log_level_offset_offset && level >= AV_LOG_FATAL)
level += *(int *) (((uint8_t *) avcl) + avc->log_level_offset_offset);
if (log_callback)
log_callback(avcl, level, fmt, vl);
}
// 回调函数指针
static void (*av_log_callback)(void*, int, const char*, va_list) =
av_log_default_callback;
//回调函数
void av_log_default_callback(void* ptr, int level, const char* fmt, va_list vl)
{
static int print_prefix = 1;
static int count;
static char prev[LINE_SZ];
AVBPrint part[4];
char line[LINE_SZ];
static int is_atty;
int type[2];
unsigned tint = 0;
if (level >= 0) {
tint = level & 0xff00;
level &= 0xff;
}
// 这里是是否输出日志的关键,只有level小于这个等级的时候才会输出日志
if (level > av_log_level)
return;
ff_mutex_lock(&mutex);
format_line(ptr, level, fmt, vl, part, &print_prefix, type);
snprintf(line, sizeof(line), "%s%s%s%s", part[0].str, part[1].str, part[2].str, part[3].str);
#if HAVE_ISATTY
if (!is_atty)
is_atty = isatty(2) ? 1 : -1;
#endif
if (print_prefix && (flags & AV_LOG_SKIP_REPEATED) && !strcmp(line, prev) &&
*line && line[strlen(line) - 1] != '\r'){
count++;
if (is_atty == 1)
fprintf(stderr, " Last message repeated %d times\r", count);
goto end;
}
if (count > 0) {
fprintf(stderr, " Last message repeated %d times\n", count);
count = 0;
}
strcpy(prev, line);
sanitize(part[0].str);
colored_fputs(type[0], 0, part[0].str);
sanitize(part[1].str);
colored_fputs(type[1], 0, part[1].str);
sanitize(part[2].str);
colored_fputs(av_clip(level >> 3, 0, NB_LEVELS - 1), tint >> 8, part[2].str);
sanitize(part[3].str);
colored_fputs(av_clip(level >> 3, 0, NB_LEVELS - 1), tint >> 8, part[3].str);
#if CONFIG_VALGRIND_BACKTRACE
if (level <= BACKTRACE_LOGLEVEL)
VALGRIND_PRINTF_BACKTRACE("%s", "");
#endif
end:
av_bprint_finalize(part+3, NULL);
ff_mutex_unlock(&mutex);
}
实际日志等级的定义
/**
* Print no output.
*/
#define AV_LOG_QUIET -8
/**
* Something went really wrong and we will crash now.
*/
#define AV_LOG_PANIC 0
/**
* Something went wrong and recovery is not possible.
* For example, no header was found for a format which depends
* on headers or an illegal combination of parameters is used.
*/
#define AV_LOG_FATAL 8
/**
* Something went wrong and cannot losslessly be recovered.
* However, not all future data is affected.
*/
#define AV_LOG_ERROR 16
/**
* Something somehow does not look correct. This may or may not
* lead to problems. An example would be the use of '-vstrict -2'.
*/
#define AV_LOG_WARNING 24
/**
* Standard information.
*/
#define AV_LOG_INFO 32
/**
* Detailed information.
*/
#define AV_LOG_VERBOSE 40
/**
* Stuff which is only useful for libav* developers.
*/
#define AV_LOG_DEBUG 48
/**
* Extremely verbose debugging, useful for libav* development.
*/
#define AV_LOG_TRACE 56
一般设置的时候我们会将日志打印等级设置为AV_LOG_DEBUG
,这个等级非常大很少有大于这个等级的日志宏定义,因此在实际中设置这个等级大多数打印都会打印出来。
取出要写的文件名字
char *pSrcFileName = argv[1];
构建AVIOContext
结构体
使用avio_open
函数构建AVIOContext
结构体
看下avio_open
函数的调用关系
1. avio_open 函数调用了 avio_open2
/**
* Create and initialize a AVIOContext for accessing the
* resource indicated by url.
* @note When the resource indicated by url has been opened in
* read+write mode, the AVIOContext can be used only for writing.
*
* @param s Used to return the pointer to the created AVIOContext.
* In case of failure the pointed to value is set to NULL.
* @param url resource to access
* @param flags flags which control how the resource indicated by url
* is to be opened
* @return >= 0 in case of success, a negative value corresponding to an
* AVERROR code in case of failure
*/
int avio_open(AVIOContext **s, const char *url, int flags);
2. avio_open函数调用了ffio_open_whitelist
3. ffio_open_whitelist函数调用了ffurl_open_whitelist 和ffio_fdopen
4. ffio_fdopen函数调用了 avio_alloc_context
/**
* Allocate and initialize an AVIOContext for buffered I/O. It must be later
* freed with avio_context_free().
*
* @param buffer Memory block for input/output operations via AVIOContext.
* The buffer must be allocated with av_malloc() and friends.
* It may be freed and replaced with a new buffer by libavformat.
* AVIOContext.buffer holds the buffer currently in use,
* which must be later freed with av_free().
* @param buffer_size The buffer size is very important for performance.
* For protocols with fixed blocksize it should be set to this blocksize.
* For others a typical size is a cache page, e.g. 4kb.
* @param write_flag Set to 1 if the buffer should be writable, 0 otherwise.
* @param opaque An opaque pointer to user-specific data.
* @param read_packet A function for refilling the buffer, may be NULL.
* For stream protocols, must never return 0 but rather
* a proper AVERROR code.
* @param write_packet A function for writing the buffer contents, may be NULL.
* The function may not change the input buffers content.
* @param seek A function for seeking to specified byte position, may be NULL.
*
* @return Allocated AVIOContext or NULL on failure.
*/
AVIOContext *avio_alloc_context(
unsigned char *buffer,
int buffer_size,
int write_flag,
void *opaque,
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
int64_t (*seek)(void *opaque, int64_t offset, int whence));
将数据写入文件
avio_write
将传入的buff
写入到AVIOContext
关联的结构体中
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
{
if (s->direct && !s->update_checksum) {
avio_flush(s);
writeout(s, buf, size);
return;
}
// 循环将传入的buf 字符串写入到s中的buf缓冲区中
while (size > 0) {
int len = FFMIN(s->buf_end - s->buf_ptr, size);
memcpy(s->buf_ptr, buf, len);
s->buf_ptr += len;
if (s->buf_ptr >= s->buf_end)
flush_buffer(s);
buf += len;
size -= len;
}
}
释放申请的资源
使用avio_close
释放申请的资源
代码实现如下
//
// Created by andrew on 2020/11/7.
//
#include <iostream>
extern "C" {
#include <libavutil/log.h>
#include <libavformat/avio.h>
}
using namespace std;
int main(int argc, char *argv[]) {
// 设置日志等级
av_log_set_level(AV_LOG_DEBUG);
if (argc < 2) {
av_log(NULL, AV_LOG_ERROR, "The count of parameter should be more than two!\n");
exit(1);
}
char *pSrcFileName = argv[1];
if (NULL == pSrcFileName) {
av_log(NULL, AV_LOG_ERROR, "invalid src filename.\n");
exit(2);
}
AVIOContext *avioCtx = NULL;
int errCode = -1;
/*
* Create and initialize a AVIOContext for accessing the
* resource indicated by url.
* */
if ((errCode = avio_open(&avioCtx, pSrcFileName, AVIO_FLAG_WRITE)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Coud not open file %s\n", pSrcFileName);
exit(3);
}
string strBuff = "hello world!\n";
avio_write(avioCtx, (const unsigned char *)(strBuff.c_str()), strBuff.length());
avio_close(avioCtx);
char tempBuff[128];
snprintf(tempBuff, sizeof(tempBuff), "cat %s", pSrcFileName);
system(tempBuff);
snprintf(tempBuff, sizeof(tempBuff), "rm %s", pSrcFileName);
system(tempBuff);
return 0;
}
#运行结果,编译时连接上对应的库文件
./ffmpeg_io_write test.txt
hello world!
#运行结果,编译时连接上对应的库文件
./ffmpeg_io_write test.txt
hello world!
read file
读文件和写文件实现基本上一样,需要改动的点是,打开文件时,将标志设置为AVIO_FLAG_READ
并将avio_write
修改为avio_read
函数。
代码实现如下:
//
// Created by andrew on 2020/11/8.
//
//
// Created by andrew on 2020/11/7.
//
#include <iostream>
extern "C" {
#include <libavutil/log.h>
#include <libavformat/avio.h>
}
using namespace std;
int main(int argc, char *argv[]) {
char tempBuff[128];
// 设置日志等级
av_log_set_level(AV_LOG_DEBUG);
if (argc < 2) {
av_log(NULL, AV_LOG_ERROR, "The count of parameter should be more than two!\n");
exit(1);
}
char *pSrcFileName = argv[1];
if (NULL == pSrcFileName) {
av_log(NULL, AV_LOG_ERROR, "invalid src filename.\n");
exit(2);
}
// 创建一个文件
snprintf(tempBuff, sizeof(tempBuff), "echo \"hello world!\n \" > %s", pSrcFileName);
system(tempBuff);
AVIOContext *avioCtx = NULL;
int errCode = -1;
/*
* Create and initialize a AVIOContext for accessing the
* resource indicated by url.
* */
if ((errCode = avio_open(&avioCtx, pSrcFileName, AVIO_FLAG_READ)) < 0) {
av_log(NULL, AV_LOG_ERROR, "Coud not open file %s\n", pSrcFileName);
exit(3);
}
unsigned char strBuff[1024];
avio_read(avioCtx, strBuff, sizeof(strBuff));
avio_close(avioCtx);
av_log(NULL, AV_LOG_DEBUG, "read file content:%s", strBuff);
memset(tempBuff, 0, sizeof(tempBuff));
snprintf(tempBuff, sizeof(tempBuff), "rm %s", pSrcFileName);
system(tempBuff);
return 0;
}
执行结果如下:
./ffmpeg_io_read test.txt
read file content:hello world!