FFmpeg中的日志以及avio实现对文件的读写功能

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!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Achilles.Wang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值