当前位置:   article > 正文

FFmpeg音视频入门-使用FFmpeg读取多媒体文件的信息C++代码实现+详解_ffmpeg c++教程

ffmpeg c++教程

读取音视频信息

设置日志等级

//  设置日志等级
av_log_set_level(AV_LOG_DEBUG);
  • 1
  • 2

参数检查

必须给出要读取的多媒体文件路径才能进行接下来的多媒体读取的任务。

if (argc != 2) {
    cout << "please input a reading file" << "argc = " << argc << endl;
    return -1;
}
char *inputFileName = argv[1];
  • 1
  • 2
  • 3
  • 4
  • 5

将多媒体文件映射为内存

// 将文件进行内存映射
/*
     * mmap()  creates  a new mapping in the virtual address space of the calling process.  The starting address for the new mapping is specified in addr.  The length argument specifies the length of
       the mapping (which must be greater than 0).

       If addr is NULL, then the kernel chooses the (page-aligned) address at which to create the mapping; this is the most portable method of creating a new mapping.  If addr is not NULL,  then  the
       kernel  takes  it  as  a  hint  about  where  to  place  the  mapping;  on  Linux,  the  kernel  will  pick  a  nearby  page  boundary  (but  always  above  or  equal to the value specified by
       /proc/sys/vm/mmap_min_addr) and attempt to create the mapping there.  If another mapping already exists there, the kernel picks a new address that may or may not depend on the hint.   The  ad‐
       dress of the new mapping is returned as the result of the call.
     * */
uint8_t *buffer = nullptr;
size_t buffer_size = 0;
int ret = av_file_map(inputFileName, &buffer, &buffer_size, 0, nullptr);
if (ret < 0) {
    cout << "av_file_map failed" << "ret = " << ret << endl;
    goto end;
}

// 记录 @av_file_map 中获取的文件内存映射的内容和长度
bufferData.ptr = buffer;
bufferData.size = buffer_size;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

FFmpeg中,linux主机上av_file_map是通过mmap实现的,也就是将文件映射到一段内存上,接下来直接操作内存地址就可以实现对文件的操作,具体的可以参考mmap函数的使用,这里也只是对mmap函数进行了一些封装。

linux主机上av_file_map的实现主要函数如下:

 ptr = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
if (ptr == MAP_FAILED) {
    err = AVERROR(errno);
    av_strerror(err, errbuf, sizeof(errbuf));
    av_log(&file_log_ctx, AV_LOG_ERROR, "Error occurred in mmap(): %s\n", errbuf);
    close(fd);
    *size = 0;
    return err;
}
*bufptr = ptr;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

初始化AVFormatContext结构体

在使用ffmpeg进行编解码时,AVFormatContext将贯穿全文。

// Allocate an AVFormatContext.
pFmtCtx = avformat_alloc_context();
if (!pFmtCtx) {
    ret = AVERROR(ENOMEM);
    goto end;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

因为是多多媒体文件的信息的读取,因此,接下来需要使用avformat_open_input打开文件的内容

// 调用  avformat_open_input 之前,要是使用文件的音视频处理,这里需要提前进行初始化,然后再调用、
// avformat_open_input 打开对应的文件
pFmtCtx->pb = pAvioCtx;
ret = avformat_open_input(&pFmtCtx, nullptr, nullptr, nullptr);
if (ret < 0) {
    cerr << "Could not open input!" << endl;
    goto end;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

然后再调用avformat_find_stream_info查询当前桢的信息

Read packets of a media file to get stream information.

ret = avformat_find_stream_info(pFmtCtx, nullptr);
if (ret < 0) {
    cerr << "Could not find stream information!" << endl;
    goto end;
}
  • 1
  • 2
  • 3
  • 4
  • 5

输出桢信息

如果文件中有桢信息,则可以使用av_dump_format函数视频文件的信息打印出来

av_dump_format(pFmtCtx, 0, inputFileName, 0);
  • 1

资源释放

end:
    avformat_close_input(&pFmtCtx);
    if (pAvioCtxBuffer && !pAvioCtx)
        av_freep(pAvioCtxBuffer);
    if (pAvioCtx)
        av_freep(&pAvioCtx->buffer);
    avio_context_free(&pAvioCtx);

    av_file_unmap(buffer, buffer_size);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

完整代码实现

//
// Created by andrew on 2020/12/13.
//
//

#include <iostream>

extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/log.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avio.h>
#include <libavutil/file.h>
#include <cstdio>
}

using namespace std;

// 为mmap buffer定义一个结构体指针用户管理数据
struct buffer_data {
    uint8_t *ptr;
    size_t size; // size left in the buffer
};

static int ReadPacket(void *pOpaque, uint8_t *pBuff, int bufferSize) {

    auto *bd = (struct buffer_data *) pOpaque;
//    #define FFMIN(a,b) ((a) > (b) ? (b) : (a))
    bufferSize = FFMIN(bufferSize, bd->size);
    if (!bufferSize) {
        cout << "ptr = " << bd->ptr << "size = " << bd->size << endl;
    }
//    将音视频数据内部的data复制到buff中
    memcpy(pBuff, bd->ptr, bufferSize);
    bd->ptr += bufferSize;
    bd->size -= bufferSize;
    return bufferSize;
}

int main(int argc, char *argv[]) {

    AVIOContext *pAvioCtx = nullptr;
    uint8_t *pAvioCtxBuffer = nullptr;
    size_t avioCtxBufferSize = 4096;
    struct buffer_data bufferData = {nullptr, 0};
    // 贯穿全局的context信息
    AVFormatContext *pFmtCtx = nullptr;
    //  设置日志等级
    av_log_set_level(AV_LOG_DEBUG);
    if (argc != 2) {
        cout << "please input a reading file" << "argc = " << argc << endl;
        return -1;
    }

    char *inputFileName = argv[1];
    // 将文件进行内存映射
    /*
     * mmap()  creates  a new mapping in the virtual address space of the calling process.  The starting address for the new mapping is specified in addr.  The length argument specifies the length of
       the mapping (which must be greater than 0).

       If addr is NULL, then the kernel chooses the (page-aligned) address at which to create the mapping; this is the most portable method of creating a new mapping.  If addr is not NULL,  then  the
       kernel  takes  it  as  a  hint  about  where  to  place  the  mapping;  on  Linux,  the  kernel  will  pick  a  nearby  page  boundary  (but  always  above  or  equal to the value specified by
       /proc/sys/vm/mmap_min_addr) and attempt to create the mapping there.  If another mapping already exists there, the kernel picks a new address that may or may not depend on the hint.   The  ad‐
       dress of the new mapping is returned as the result of the call.
     * */
    uint8_t *buffer = nullptr;
    size_t buffer_size = 0;
    int ret = av_file_map(inputFileName, &buffer, &buffer_size, 0, nullptr);
    if (ret < 0) {
        cout << "av_file_map failed" << "ret = " << ret << endl;
        goto end;
    }

    // 记录 @av_file_map 中获取的文件内存映射的内容和长度
    bufferData.ptr = buffer;
    bufferData.size = buffer_size;

    // Allocate an AVFormatContext.

    pFmtCtx = avformat_alloc_context();
    if (!pFmtCtx) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    pAvioCtxBuffer = (uint8_t *) av_malloc(avioCtxBufferSize);
    if (!pAvioCtxBuffer) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    pAvioCtx = avio_alloc_context(pAvioCtxBuffer, avioCtxBufferSize, 0, &bufferData, &ReadPacket,
                                  nullptr, nullptr);
    if (!pAvioCtx) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
    // 调用  avformat_open_input 之前,要是使用文件的音视频处理,这里需要提前进行初始化,然后再调用、
    // avformat_open_input 打开对应的文件
    pFmtCtx->pb = pAvioCtx;
    ret = avformat_open_input(&pFmtCtx, nullptr, nullptr, nullptr);
    if (ret < 0) {
        cerr << "Could not open input!" << endl;
        goto end;
    }

    ret = avformat_find_stream_info(pFmtCtx, nullptr);
    if (ret < 0) {
        cerr << "Could not find stream information!" << endl;
        goto end;
    }

    av_dump_format(pFmtCtx, 0, inputFileName, 0);


    cout << "avio reading demo success" << endl;
end:
    avformat_close_input(&pFmtCtx);
    if (pAvioCtxBuffer && !pAvioCtx)
        av_freep(pAvioCtxBuffer);
    if (pAvioCtx)
        av_freep(&pAvioCtx->buffer);
    avio_context_free(&pAvioCtx);

    av_file_unmap(buffer, buffer_size);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/350741
推荐阅读
  

闽ICP备14008679号