FFmpeg中的日志以及avio实现对文件的读写功能
ffmpeg日志
ffmpeg日志,接口都定義在log.h,使用的庫(kù)是libavutil
使用時(shí)一定要加上對(duì)頭文件extern "C"外部,否則會(huì)報(bào)對(duì)應(yīng)的函數(shù)沒有定義
// // 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; }執(zhí)行結(jié)果:
Hello world!write file
第一步先設(shè)置日志等級(jí),這里追溯下日志等級(jí)設(shè)置的實(shí)現(xiàn)。
av_log_set_level設(shè)置日志等級(jí)
前期追溯過的函數(shù)后面將不會(huì)進(jìn)行重復(fù)追蹤定位。
av_log_set_level(AV_LOG_DEBUG);函數(shù)點(diǎn)擊進(jìn)入函數(shù),函數(shù)實(shí)現(xiàn)如下,只是設(shè)置了一個(gè)全局變量,用于記錄日志的等級(jí)
void av_log_set_level(int level) {av_log_level = level; }再看下實(shí)際使用時(shí),函數(shù)是如何判定日志是否需要輸出的
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); } //實(shí)際調(diào)用 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); } // 回調(diào)函數(shù)指針 static void (*av_log_callback)(void*, int, const char*, va_list) =av_log_default_callback;//回調(diào)函數(shù) 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;}// 這里是是否輸出日志的關(guān)鍵,只有l(wèi)evel小于這個(gè)等級(jí)的時(shí)候才會(huì)輸出日志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_ISATTYif (!is_atty)is_atty = isatty(2) ? 1 : -1; #endifif (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_BACKTRACEif (level <= BACKTRACE_LOGLEVEL)VALGRIND_PRINTF_BACKTRACE("%s", ""); #endif end:av_bprint_finalize(part+3, NULL);ff_mutex_unlock(&mutex); }實(shí)際日志等級(jí)的定義
/*** 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一般設(shè)置的時(shí)候我們會(huì)將日志打印等級(jí)設(shè)置為AV_LOG_DEBUG,這個(gè)等級(jí)非常大很少有大于這個(gè)等級(jí)的日志宏定義,因此在實(shí)際中設(shè)置這個(gè)等級(jí)大多數(shù)打印都會(huì)打印出來。
取出要寫的文件名字
char *pSrcFileName = argv[1];構(gòu)建AVIOContext結(jié)構(gòu)體
使用avio_open函數(shù)構(gòu)建AVIOContext結(jié)構(gòu)體
看下avio_open函數(shù)的調(diào)用關(guān)系
1. avio_open 函數(shù)調(diào)用了 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函數(shù)調(diào)用了ffio_open_whitelist 3. ffio_open_whitelist函數(shù)調(diào)用了ffurl_open_whitelist 和ffio_fdopen 4. ffio_fdopen函數(shù)調(diào)用了 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));將數(shù)據(jù)寫入文件
avio_write將傳入的buff寫入到AVIOContext關(guān)聯(lián)的結(jié)構(gòu)體中
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;}// 循環(huán)將傳入的buf 字符串寫入到s中的buf緩沖區(qū)中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;} }釋放申請(qǐng)的資源
使用avio_close釋放申請(qǐng)的資源
代碼實(shí)現(xiàn)如下
// // 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[]) {// 設(shè)置日志等級(jí)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; } #運(yùn)行結(jié)果,編譯時(shí)連接上對(duì)應(yīng)的庫(kù)文件 ./ffmpeg_io_write test.txt hello world! #運(yùn)行結(jié)果,編譯時(shí)連接上對(duì)應(yīng)的庫(kù)文件 ./ffmpeg_io_write test.txt hello world!read file
讀文件和寫文件實(shí)現(xiàn)基本上一樣,需要改動(dòng)的點(diǎn)是,打開文件時(shí),將標(biāo)志設(shè)置為AVIO_FLAG_READ并將avio_write修改為avio_read函數(shù)。
代碼實(shí)現(xiàn)如下:
// // 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];// 設(shè)置日志等級(jí)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);}// 創(chuàng)建一個(gè)文件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; }執(zhí)行結(jié)果如下:
./ffmpeg_io_read test.txt read file content:hello world!總結(jié)
以上是生活随笔為你收集整理的FFmpeg中的日志以及avio实现对文件的读写功能的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 23种设计模式C++源码与UML实现--
- 下一篇: FFmpeg--av_register_