FFMPEG详解(完整版)

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

如果你需要这些资料,可以戳这里获取

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
#include <sys/time.h>
 
#include "libavutil/avstring.h"
#include "libavformat/avformat.h"
#include "libavdevice/avdevice.h"
#include "libavutil/opt.h"
#include "libswscale/swscale.h"
 
#define DECODED_AUDIO_BUFFER_SIZE            192000
 
struct options
{
    int streamId;
    int frames;
    int nodec;
    int bplay;
    int thread_count;
    int64_t lstart;
    char finput[256];
    char foutput1[256];
    char foutput2[256];
};
 
int parse_options(struct options *opts, int argc, char** argv)
{
    int optidx;
    char *optstr;
 
    if (argc < 2) return -1;
 
    opts->streamId = -1;
    opts->lstart = -1;
    opts->frames = -1;
    opts->foutput1[0] = 0;
    opts->foutput2[0] = 0;
    opts->nodec = 0;
    opts->bplay = 0;
    opts->thread_count = 0;
    [strcpy](https://bbs.csdn.net/topics/618679757)(opts->finput, argv[1]);
 
    optidx = 2;
    while (optidx < argc)
    {
        optstr = argv[optidx++];
        if (*optstr++ != '-') return -1;
        switch (*optstr++)
        {
        case 's':  //< stream id
            opts->streamId = [atoi](https://bbs.csdn.net/topics/618679757)(optstr);
            break;
        case 'f':  //< frames
            opts->frames = [atoi](https://bbs.csdn.net/topics/618679757)(optstr);
            break;
        case 'k':  //< skipped
            opts->lstart = atoll(optstr);
            break;
        case 'o':  //< output
            [strcpy](https://bbs.csdn.net/topics/618679757)(opts->foutput1, optstr);
            [strcat](https://bbs.csdn.net/topics/618679757)(opts->foutput1, ".mpg");
            [strcpy](https://bbs.csdn.net/topics/618679757)(opts->foutput2, optstr);
            [strcat](https://bbs.csdn.net/topics/618679757)(opts->foutput2, ".raw");
            break;
        case 'n': //decoding and output options
            if ([strcmp](https://bbs.csdn.net/topics/618679757)("dec", optstr) == 0)
                opts->nodec = 1;
            break;
        case 'p':
            opts->bplay = 1;
            break;
        case 't':
            opts->thread_count = [atoi](https://bbs.csdn.net/topics/618679757)(optstr);
            break;
        default:
            return -1;
        }
    }
 
    return 0;
}
 
void show_help(char* program)
{
    [printf](https://bbs.csdn.net/topics/618679757)("Simple FFMPEG test program\n");
    [printf](https://bbs.csdn.net/topics/618679757)("Usage: %s inputfile [-sstreamid [-fframes] [-kskipped] [-ooutput_filename(without extension)] [-ndec] [-p] [-tthread_count]]\n",
           program);
    return;
}
 
static void log_callback(void* ptr, int level, const char* fmt, va_list vl)
{
    [vfprintf](https://bbs.csdn.net/topics/618679757)(stdout, fmt, vl);
}
 
/*
* audio renderer code (oss)
*/
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/soundcard.h>
 
#define OSS_DEVICE "/dev/dsp0"
 
struct audio_dsp
{
    int audio_fd;
    int channels;
    int format;
    int speed;
};
int map_formats(enum AVSampleFormat format)
{
    switch(format)
    {
        case AV_SAMPLE_FMT_U8:
            return AFMT_U8;
        case AV_SAMPLE_FMT_S16:
            return AFMT_S16_LE;
        default:
            return AFMT_U8; 
    }
}
int set_audio(struct audio_dsp* dsp)
{
    if (dsp->audio_fd == -1)
    {
        [printf](https://bbs.csdn.net/topics/618679757)("Invalid audio dsp id!\n");
        return -1;
    }    
 
    if (-1 == ioctl(dsp->audio_fd, SNDCTL_DSP_SETFMT, &dsp->format))
    {
        [printf](https://bbs.csdn.net/topics/618679757)("Failed to set dsp format!\n");
        return -1;
    }
 
    if (-1 == ioctl(dsp->audio_fd, SNDCTL_DSP_CHANNELS, &dsp->channels))
    {
        [printf](https://bbs.csdn.net/topics/618679757)("Failed to set dsp format!\n");
        return -1;
    }
 
    if (-1 == ioctl(dsp->audio_fd, SNDCTL_DSP_SPEED, &dsp->speed))
    {
        [printf](https://bbs.csdn.net/topics/618679757)("Failed to set dsp format!\n");
        return -1;
    }    
    return 0;
}
 
int play_pcm(struct audio_dsp* dsp, unsigned char *buf, int size)
{
    if (dsp->audio_fd == -1)
    {
        [printf](https://bbs.csdn.net/topics/618679757)("Invalid audio dsp id!\n");
        return -1;
    }
 
    if (-1 == write(dsp->audio_fd, buf, size))
    {
        [printf](https://bbs.csdn.net/topics/618679757)("Failed to write audio dsp!\n");
        return -1;
    }
 
    return 0;
}
/* audio renderer code end */
 
/* video renderer code*/
#include <linux/fb.h>
#include <sys/mman.h>
 
#define FB_DEVICE "/dev/fb0"
 
enum pic_format
{
    eYUV_420_Planer,
};
struct video_fb
{
    int video_fd;
    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;
    unsigned char *fbp;
    AVFrame *frameRGB;
    struct 
    {
        int x;
        int y;
    } video_pos;
};
 
int open_video(struct video_fb *fb, int x, int y)
{
    int screensize;
    fb->video_fd = open(FB_DEVICE, O_WRONLY);
    if (fb->video_fd == -1) return -1;
 
    if (ioctl(fb->video_fd, FBIOGET_FSCREENINFO, &fb->finfo)) return -2;
    if (ioctl(fb->video_fd, FBIOGET_VSCREENINFO, &fb->vinfo)) return -2;
 
 
    [printf](https://bbs.csdn.net/topics/618679757)("video device: resolution %dx%d, %dbpp\n", fb->vinfo.xres, fb->vinfo.yres, fb->vinfo.bits_per_pixel);
    screensize = fb->vinfo.xres * fb->vinfo.yres * fb->vinfo.bits_per_pixel / 8;
    fb->fbp = (unsigned char *) mmap(0, screensize, PROT_READ|PROT_WRITE, MAP_SHARED, fb->video_fd, 0);
    if (fb->fbp == -1) return -3;
 
    if (x >= fb->vinfo.xres || y >= fb->vinfo.yres)
    {
        return -4;
    }
    else
    {
        fb->video_pos.x = x;
        fb->video_pos.y = y;
    }
 
    fb->frameRGB = avcodec_alloc_frame();
    if (!fb->frameRGB) return -5;
 
    return 0;
}
 
/* only 420P supported now */
int show_picture(struct video_fb *fb, AVFrame *frame, int width, int height, enum pic_format format)
{
    struct SwsContext *sws;
    int i;
    unsigned char *dest;
    unsigned char *src;
 
    if (fb->video_fd == -1) return -1;
    if ((fb->video_pos.x >= fb->vinfo.xres) || (fb->video_pos.y >= fb->vinfo.yres)) return -2;
 
    if (fb->video_pos.x + width > fb->vinfo.xres)
    {
        width = fb->vinfo.xres - fb->video_pos.x;
    }
    if (fb->video_pos.y + height > fb->vinfo.yres)
    {
        height = fb->vinfo.yres - fb->video_pos.y;
    }
 
    if (format == PIX_FMT_YUV420P)
    {
        sws = sws_getContext(width, height, format, width, height, PIX_FMT_RGB32, SWS_FAST_BILINEAR, NULL, NULL, NULL);
        if (sws == 0)
        {
            return -3;
        }
        if (sws_scale(sws, frame->data, frame->linesize, 0, height, fb->frameRGB->data, fb->frameRGB->linesize))
        {
            return -3;
        }
 
        dest = fb->fbp + (fb->video_pos.x+fb->vinfo.xoffset) * (fb->vinfo.bits_per_pixel/8) +(fb->video_pos.y+fb->vinfo.yoffset) * fb->finfo.line_length;
        for (i = 0; i < height; i++)
        {
            [memcpy](https://bbs.csdn.net/topics/618679757)(dest, src, width*4);
            src += fb->frameRGB->linesize[0];
            dest += fb->finfo.line_length;
        }
    }
    return 0;
}
 
void close_video(struct video_fb *fb)
{
    if (fb->video_fd != -1) 
    {
        munmap(fb->fbp, fb->vinfo.xres * fb->vinfo.yres * fb->vinfo.bits_per_pixel / 8);
        close(fb->video_fd);
        fb->video_fd = -1;
    }
}
/* video renderer code end */
 
int main(int argc, char **argv)
{
    AVFormatContext* pCtx = 0;
    AVCodecContext *pCodecCtx = 0;
    AVCodec *pCodec = 0;
    AVPacket packet;
    AVFrame *pFrame = 0;
    FILE *fpo1 = NULL;
    FILE *fpo2 = NULL;
    int nframe;
    int err;
    int got_picture;
    int picwidth, picheight, linesize;
    unsigned char *pBuf;
    int i;
    int64_t timestamp;
    struct options opt;
    int usefo = 0;
    struct audio_dsp dsp;
    struct video_fb fb;
    int dusecs;
    float usecs1 = 0;
    float usecs2 = 0;
    struct timeval elapsed1, elapsed2;
    int decoded = 0;
 
    av_register_all();
 
    av_log_set_callback(log_callback);
    av_log_set_level(50);
 
    if (parse_options(&opt, argc, argv) < 0 || ([strlen](https://bbs.csdn.net/topics/618679757)(opt.finput) == 0))
    {
        show_help(argv[0]);
        return 0;
    }
 
 
    err = avformat_open_input(&pCtx, opt.finput, 0, 0);
    if (err < 0)
    {
        [printf](https://bbs.csdn.net/topics/618679757)("\n->(avformat_open_input)\tERROR:\t%d\n", err);
        goto fail;
    }
    err = avformat_find_stream_info(pCtx, 0);
    if (err < 0)
    {
        [printf](https://bbs.csdn.net/topics/618679757)("\n->(avformat_find_stream_info)\tERROR:\t%d\n", err);
        goto fail;
    }
    if (opt.streamId < 0)
    {
        av_dump_format(pCtx, 0, pCtx->filename, 0);
        goto fail;
    }
    else
    {
        [printf](https://bbs.csdn.net/topics/618679757)("\n extra data in Stream %d (%dB):", opt.streamId, pCtx->streams[opt.streamId]->codec->extradata_size);
        for (i = 0; i < pCtx->streams[opt.streamId]->codec->extradata_size; i++)
        {
            if (i%16 == 0) [printf](https://bbs.csdn.net/topics/618679757)("\n");
            [printf](https://bbs.csdn.net/topics/618679757)("%2x  ", pCtx->streams[opt.streamId]->codec->extradata[i]);
        }
    }
    /* try opening output files */
    if ([strlen](https://bbs.csdn.net/topics/618679757)(opt.foutput1) && [strlen](https://bbs.csdn.net/topics/618679757)(opt.foutput2))
    {
        fpo1 = [fopen](https://bbs.csdn.net/topics/618679757)(opt.foutput1, "wb");
        fpo2 = [fopen](https://bbs.csdn.net/topics/618679757)(opt.foutput2, "wb");
        if (!fpo1 || !fpo2)
        {
            [printf](https://bbs.csdn.net/topics/618679757)("\n->error opening output files\n");
            goto fail;
        }
        usefo = 1;
    }
    else
    {
        usefo = 0;
    }
 
    if (opt.streamId >= pCtx->nb_streams)
    {
        [printf](https://bbs.csdn.net/topics/618679757)("\n->StreamId\tERROR\n");
        goto fail;
    }
 
    if (opt.lstart > 0)
    {
        err = av_seek_frame(pCtx, opt.streamId, opt.lstart, AVSEEK_FLAG_ANY);
        if (err < 0)
        {
            [printf](https://bbs.csdn.net/topics/618679757)("\n->(av_seek_frame)\tERROR:\t%d\n", err);
            goto fail;
        }
    }
 
    /* for decoder configuration */
    if (!opt.nodec)
    {
        /* prepare codec */
        pCodecCtx = pCtx->streams[opt.streamId]->codec;
 
        if (opt.thread_count <= 16 && opt.thread_count > 0 )
        {
            pCodecCtx->thread_count = opt.thread_count;
            pCodecCtx->thread_type = FF_THREAD_FRAME;
        }
        pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
        if (!pCodec)
        {
            [printf](https://bbs.csdn.net/topics/618679757)("\n->can not find codec!\n");
            goto fail;
        }
        err = avcodec_open2(pCodecCtx, pCodec, 0);
        if (err < 0)
        {
            [printf](https://bbs.csdn.net/topics/618679757)("\n->(avcodec_open)\tERROR:\t%d\n", err);
            goto fail;
        }
        pFrame = avcodec_alloc_frame();        
 
        /* prepare device */
        if (opt.bplay)
        {
            /* audio devices */
            dsp.audio_fd = open(OSS_DEVICE, O_WRONLY);
            if (dsp.audio_fd == -1)
            {
                [printf](https://bbs.csdn.net/topics/618679757)("\n-> can not open audio device\n");
                goto fail;
            }
            dsp.channels = pCodecCtx->channels;
            dsp.speed = pCodecCtx->sample_rate;
            dsp.format = map_formats(pCodecCtx->sample_fmt);
            if (set_audio(&dsp) < 0)
            {
                [printf](https://bbs.csdn.net/topics/618679757)("\n-> can not set audio device\n");
                goto fail;
            }
            /* video devices */
            if (open_video(&fb, 0, 0) != 0)
            {
            	[printf](https://bbs.csdn.net/topics/618679757)("\n-> can not open video device\n");
            	goto fail;
            }
        }
    }
 
    nframe = 0;
    while(nframe < opt.frames || opt.frames == -1)
    {
        gettimeofday(&elapsed1, NULL);
        err = av_read_frame(pCtx, &packet);
        if (err < 0)
        {
            [printf](https://bbs.csdn.net/topics/618679757)("\n->(av_read_frame)\tERROR:\t%d\n", err);
            break;
        }
        gettimeofday(&elapsed2, NULL);
        dusecs = (elapsed2.tv_sec - elapsed1.tv_sec)*1000000 + (elapsed2.tv_usec - elapsed1.tv_usec);
        usecs2 += dusecs;        
        timestamp = av_rescale_q(packet.dts, pCtx->streams[packet.stream_index]->time_base, (AVRational){1, AV_TIME_BASE});
        [printf](https://bbs.csdn.net/topics/618679757)("\nFrame No %5d stream#%d\tsize %6dB, timestamp:%6lld, dts:%6lld, pts:%6lld, ", nframe++, packet.stream_index, packet.size, 
               timestamp, packet.dts, packet.pts);
 
        if (packet.stream_index == opt.streamId)
        {
#if 0 
            for (i = 0; i < 16; /*packet.size;*/ i++)
            {
                if (i%16 == 0) [printf](https://bbs.csdn.net/topics/618679757)("\n pktdata: ");
                [printf](https://bbs.csdn.net/topics/618679757)("%2x  ", packet.data[i]);
            }
            [printf](https://bbs.csdn.net/topics/618679757)("\n");
#endif            
            if (usefo)
            {
                [fwrite](https://bbs.csdn.net/topics/618679757)(packet.data, packet.size, 1, fpo1);
                [fflush](https://bbs.csdn.net/topics/618679757)(fpo1);
            }
 
            if (pCtx->streams[opt.streamId]->codec->codec_type == AVMEDIA_TYPE_VIDEO && !opt.nodec)
            {
                picheight = pCtx->streams[opt.streamId]->codec->height;
                picwidth = pCtx->streams[opt.streamId]->codec->width;
 
                gettimeofday(&elapsed1, NULL); 
                avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, &packet);
                decoded++;
                gettimeofday(&elapsed2, NULL);
                dusecs = (elapsed2.tv_sec - elapsed1.tv_sec)*1000000 + (elapsed2.tv_usec - elapsed1.tv_usec);
                usecs1 += dusecs;
 
                if (got_picture)
                {
                    [printf](https://bbs.csdn.net/topics/618679757)("[Video: type %d, ref %d, pts %lld, pkt_pts %lld, pkt_dts %lld]", 
                            pFrame->pict_type, pFrame->reference, pFrame->pts, pFrame->pkt_pts, pFrame->pkt_dts);
 
                    if (pCtx->streams[opt.streamId]->codec->pix_fmt == PIX_FMT_YUV420P)
                    {
                        if (usefo)
                        {
                            linesize = pFrame->linesize[0];
                            pBuf = pFrame->data[0];
                            for (i = 0; i < picheight; i++)
                            {
                                [fwrite](https://bbs.csdn.net/topics/618679757)(pBuf, picwidth, 1, fpo2);
                                pBuf += linesize;
                            }
 
                            linesize = pFrame->linesize[1];
                            pBuf = pFrame->data[1];
                            for (i = 0; i < picheight/2; i++)
                            {
                                [fwrite](https://bbs.csdn.net/topics/618679757)(pBuf, picwidth/2, 1, fpo2);
                                pBuf += linesize;
                            }           
 
                            linesize = pFrame->linesize[2];
                            pBuf = pFrame->data[2];
                            for (i = 0; i < picheight/2; i++)
                            {
                                [fwrite](https://bbs.csdn.net/topics/618679757)(pBuf, picwidth/2, 1, fpo2);
                                pBuf += linesize;
                            }  
                            [fflush](https://bbs.csdn.net/topics/618679757)(fpo2);
                        } 
 
                        if (opt.bplay)
                        {
                            /* show picture */
                        	show_picture(&fb, pFrame, picheight, picwidth, PIX_FMT_YUV420P);
                        }
                    }
                }
                av_free_packet(&packet);
            }
            else if (pCtx->streams[opt.streamId]->codec->codec_type == AVMEDIA_TYPE_AUDIO && !opt.nodec)
            {
                int got;
 
                gettimeofday(&elapsed1, NULL);
                avcodec_decode_audio4(pCodecCtx, pFrame, &got, &packet);
                decoded++;
                gettimeofday(&elapsed2, NULL);
                dusecs = (elapsed2.tv_sec - elapsed1.tv_sec)*1000000 + (elapsed2.tv_usec - elapsed1.tv_usec);
                usecs1 += dusecs;
 
				if (got)
				{
                    [printf](https://bbs.csdn.net/topics/618679757)("[Audio: %5dB raw data, decoding time: %d]", pFrame->linesize[0], dusecs);
                    if (usefo)
                    {
                        [fwrite](https://bbs.csdn.net/topics/618679757)(pFrame->data[0],  pFrame->linesize[0], 1, fpo2);
                        [fflush](https://bbs.csdn.net/topics/618679757)(fpo2);
                    }
                    if (opt.bplay)
                    {
                        play_pcm(&dsp, pFrame->data[0],  pFrame->linesize[0]);
                    }
				}
            }
        }
    }  
 
    if (!opt.nodec && pCodecCtx)
    {
        avcodec_close(pCodecCtx);
    }
 
    [printf](https://bbs.csdn.net/topics/618679757)("\n%d frames parsed, average %.2f us per frame\n", nframe, usecs2/nframe);
    [printf](https://bbs.csdn.net/topics/618679757)("%d frames decoded, average %.2f us per frame\n", decoded, usecs1/decoded);
 
fail:
    if (pCtx)
    {
        avformat_close_input(&pCtx);
    }
    if (fpo1)
    {
        [fclose](https://bbs.csdn.net/topics/618679757)(fpo1);
    }
    if (fpo2)
    {
        [fclose](https://bbs.csdn.net/topics/618679757)(fpo2);
    }
    if (!pFrame)
    {
        av_free(pFrame);
    }
    if (!usefo && (dsp.audio_fd != -1))
    {
        close(dsp.audio_fd);
    }
    if (!usefo && (fb.video_fd != -1))
    {
    	close_video(&fb);
    }
    return 0;
}

这一小段代码可以实现的功能包括:

  • 打开一个多媒体文件并获取基本的媒体信息。
  • 获取编码器句柄。
  • 根据给定的时间标签进行一个跳转。
  • 读取数据帧。
  • 解码音频帧或者视频帧。
  • 关闭多媒体文件。

这些功能足以支持一个功能强大的多媒体播放器,因为最复杂的解复用、解码、数据分析过程已经在FFMpeg内部实现了,需要关注的仅剩同步问题。

用户接口

数据结构

基本概念

编解码器、数据帧、媒体流和容器是数字媒体处理系统的四个基本概念。

首先需要统一术语:

  • 容器/文件(Conainer/File):即特定格式的多媒体文件。
  • 媒体流(Stream):指时间轴上的一段连续数据,如一段声音数据,一段视频数据或一段字幕数据,可以是压缩的,也可以是非压缩的,压缩的数据需要关联特定的编解码器。
  • 数据帧/数据包(Frame/Packet):通常,一个媒体流由大量的数据帧组成,对于压缩数据,帧对应着编解码器的最小处理单元。通常,分属于不同媒体流的数据帧交错复用于容器之中,参见交错
  • 编解码器:编解码器以帧为单位实现压缩数据和原始数据之间的相互转换。

在FFMPEG中,使用AVFormatContext、AVStream、AVCodecContext、AVCodec及AVPacket等结构来抽象这些基本要素,它们的关系如下图所示:

AVCodecContext

这是一个描述编解码器上下文的数据结构,包含了众多编解码器需要的参数信息,如下列出了部分比较重要的域:

typedef struct AVCodecContext {
 
    ......
 
    /**
     * some codecs need / can use extradata like Huffman tables.
     * mjpeg: Huffman tables
     * rv10: additional flags
     * mpeg4: global headers (they can be in the bitstream or here)
     * The allocated memory should be FF_INPUT_BUFFER_PADDING_SIZE bytes larger
     * than extradata_size to avoid prolems if it is read with the bitstream reader.
     * The bytewise contents of extradata must not depend on the architecture or CPU endianness.
     * - encoding: Set/allocated/freed by libavcodec.
     * - decoding: Set/allocated/freed by user.
     */
    uint8_t *extradata;
    int extradata_size;
    /**
     * This is the fundamental unit of time (in seconds) in terms
     * of which frame timestamps are represented. For fixed-fps content,
     * timebase should be 1/framerate and timestamp increments should be
     * identically 1.
     * - encoding: MUST be set by user.
     * - decoding: Set by libavcodec.
     */
    AVRational time_base;
 
    /* video only */
    /**
     * picture width / height.
     * - encoding: MUST be set by user.
     * - decoding: Set by libavcodec.
     * Note: For compatibility it is possible to set this instead of
     * coded_width/height before decoding.
     */
    int width, height;
 
    ......
 
    /* audio only */
    int sample_rate; ///< samples per second
    int channels;    ///< number of audio channels
 
    /**
     * audio sample format
     * - encoding: Set by user.
     * - decoding: Set by libavcodec.
     */
    enum SampleFormat sample_fmt;  ///< sample format
 
    /* The following data should not be initialized. */
    /**
     * Samples per packet, initialized when calling 'init'.
     */
    int frame_size;
    int frame_number;   ///< audio or video frame number
 
    ......
 
    char codec_name[32];
    enum AVMediaType codec_type; /* see AVMEDIA_TYPE_xxx */
    enum CodecID codec_id; /* see CODEC_ID_xxx */
 
    /**
     * fourcc (LSB first, so "ABCD" -> ('D'<<24) + ('C'<<16) + ('B'<<8) + 'A').
     * This is used to work around some encoder bugs.
     * A demuxer should set this to what is stored in the field used to identify the codec.
     * If there are multiple such fields in a container then the demuxer should choose the one
     * which maximizes the information about the used codec.
     * If the codec tag field in a container is larger then 32 bits then the demuxer should
     * remap the longer ID to 32 bits with a table or other structure. Alternatively a new
     * extra_codec_tag + size could be added but for this a clear advantage must be demonstrated
     * first.
     * - encoding: Set by user, if not then the default based on codec_id will be used.
     * - decoding: Set by user, will be converted to uppercase by libavcodec during init.
     */
    unsigned int codec_tag;            
 
    ......
 
    /**
     * Size of the frame reordering buffer in the decoder.
     * For MPEG-2 it is 1 IPB or 0 low delay IP.
     * - encoding: Set by libavcodec.
     * - decoding: Set by libavcodec.
     */
    int has_b_frames;
 
    /**
     * number of bytes per packet if constant and known or 0
     * Used by some WAV based audio codecs.
     */
    int block_align;
 
    ......
 
    /**
     * bits per sample/pixel from the demuxer (needed for huffyuv).
     * - encoding: Set by libavcodec.
     * - decoding: Set by user.
     */
     int bits_per_coded_sample;  
 
     ......
 
} AVCodecContext;

如果是单纯使用libavcodec,这部分信息需要调用者进行初始化;如果是使用整个FFMPEG库,这部分信息在调用avformat_open_input和avformat_find_stream_info的过程中根据文件的头信息及媒体流内的头部信息完成初始化。其中几个主要域的释义如下:

  1. extradata/extradata_size:这个buffer中存放了解码器可能会用到的额外信息,在av_read_frame中填充。一般来说,首先,某种具体格式的demuxer在读取格式头信息的时候会填充extradata,其次,如果demuxer没有做这个事情,比如可能在头部压根儿就没有相关的编解码信息,则相应的parser会继续从已经解复用出来的媒体流中继续寻找。在没有找到任何额外信息的情况下,这个buffer指针为空。
  2. time_base:编解码器的时间基准,实际上就是视频的帧率(或场率)。
  3. width/height:视频的宽和高。
  4. sample_rate/channels:音频的采样率和信道数目。
  5. sample_fmt: 音频的原始采样格式。
  6. codec_name/codec_type/codec_id/codec_tag:编解码器的信息。

AVStream

该结构体描述一个媒体流,定义如下:

typedef struct AVStream {
    int index;    /**< stream index in AVFormatContext */
    int id;       /**< format-specific stream ID */
    AVCodecContext *codec; /**< codec context */
    /**
     * Real base framerate of the stream.
     * This is the lowest framerate with which all timestamps can be
     * represented accurately (it is the least common multiple of all
     * framerates in the stream). Note, this value is just a guess!
     * For example, if the time base is 1/90000 and all frames have either
     * approximately 3600 or 1800 timer ticks, then r_frame_rate will be 50/1.
     */
    AVRational r_frame_rate;
 
    ......
 
    /**
     * This is the fundamental unit of time (in seconds) in terms
     * of which frame timestamps are represented. For fixed-fps content,
     * time base should be 1/framerate and timestamp increments should be 1.
     */
    AVRational time_base;
 
    ......
 
    /**
     * Decoding: pts of the first frame of the stream, in stream time base.
     * Only set this if you are absolutely 100% sure that the value you set
     * it to really is the pts of the first frame.
     * This may be undefined (AV_NOPTS_VALUE).
     * @note The ASF header does NOT contain a correct start_time the ASF
     * demuxer must NOT set this.
     */
    int64_t start_time;
    /**
     * Decoding: duration of the stream, in stream time base.
     * If a source file does not specify a duration, but does specify
     * a bitrate, this value will be estimated from bitrate and file size.
     */
    int64_t duration;
 
#if LIBAVFORMAT_VERSION_INT < (53<<16)
    char language[4]; /** ISO 639-2/B 3-letter language code (empty string if undefined) */
#endif
 
    /* av_read_frame() support */
    enum AVStreamParseType need_parsing;
    struct AVCodecParserContext *parser;
 
    ......
 
    /* av_seek_frame() support */
    AVIndexEntry *index_entries; /**< Only used if the format does not
                                    support seeking natively. */
    int nb_index_entries;
    unsigned int index_entries_allocated_size;
 
    int64_t nb_frames;                 ///< number of frames in this stream if known or 0
 
    ......
 
    /**
     * Average framerate
     */
    AVRational avg_frame_rate;
    ......
} AVStream;

主要域的释义如下,其中大部分域的值可以由avformat_open_input根据文件头的信息确定,缺少的信息需要通过调用avformat_find_stream_info读帧及软解码进一步获取:

  1. index/id:index对应流的索引,这个数字是自动生成的,根据index可以从AVFormatContext::streams表中索引到该流;而id则是流的标识,依赖于具体的容器格式。比如对于MPEG TS格式,id就是pid。
  2. time_base:流的时间基准,是一个实数,该流中媒体数据的pts和dts都将以这个时间基准为粒度。通常,使用av_rescale/av_rescale_q可以实现不同时间基准的转换。
  3. start_time:流的起始时间,以流的时间基准为单位,通常是该流中第一个帧的pts。
  4. duration:流的总时间,以流的时间基准为单位。
  5. need_parsing:对该流parsing过程的控制域。
  6. nb_frames:流内的帧数目。
  7. r_frame_rate/framerate/avg_frame_rate:帧率相关。
  8. codec:指向该流对应的AVCodecContext结构,调用avformat_open_input时生成。
  9. parser:指向该流对应的AVCodecParserContext结构,调用avformat_find_stream_info时生成。。

AVFormatContext

这个结构体描述了一个媒体文件或媒体流的构成和基本信息,定义如下:

typedef struct AVFormatContext {
    const AVClass *av_class; /**< Set by avformat_alloc_context. */
    /* Can only be iformat or oformat, not both at the same time. */
    struct AVInputFormat *iformat;
    struct AVOutputFormat *oformat;
    void *priv_data;
    ByteIOContext *pb;
    unsigned int nb_streams;
    AVStream *streams[MAX_STREAMS];
    char filename[1024]; /**< input or output filename */
    /* stream info */
    int64_t timestamp;
#if LIBAVFORMAT_VERSION_INT < (53<<16)
    char title[512];
    char author[512];
    char copyright[512];
    char comment[512];
    char album[512];
    int year;  /**< ID3 year, 0 if none */
    int track; /**< track number, 0 if none */
    char genre[32]; /**< ID3 genre */
#endif
 
    int ctx_flags; /**< Format-specific flags, see AVFMTCTX_xx */
    /* private data for pts handling (do not modify directly). */
    /** This buffer is only needed when packets were already buffered but
       not decoded, for example to get the codec parameters in MPEG
       streams. */
    struct AVPacketList *packet_buffer;
 
    /** Decoding: position of the first frame of the component, in
       AV_TIME_BASE fractional seconds. NEVER set this value directly:
       It is deduced from the AVStream values.  */
    int64_t start_time;
    /** Decoding: duration of the stream, in AV_TIME_BASE fractional
       seconds. Only set this value if you know none of the individual stream
       durations and also dont set any of them. This is deduced from the
       AVStream values if not set.  */
    int64_t duration;
    /** decoding: total file size, 0 if unknown */
    int64_t file_size;
    /** Decoding: total stream bitrate in bit/s, 0 if not
       available. Never set it directly if the file_size and the
       duration are known as FFmpeg can compute it automatically. */
    int bit_rate;
 
    /* av_read_frame() support */
    AVStream *cur_st;
#if LIBAVFORMAT_VERSION_INT < (53<<16)
    const uint8_t *cur_ptr_deprecated;
    int cur_len_deprecated;
    AVPacket cur_pkt_deprecated;
#endif
 
    /* av_seek_frame() support */
    int64_t data_offset; /** offset of the first packet */
    int index_built;
 
    int mux_rate;
    unsigned int packet_size;
    int preload;
    int max_delay;
 
#define AVFMT_NOOUTPUTLOOP -1
#define AVFMT_INFINITEOUTPUTLOOP 0
    /** number of times to loop output in formats that support it */
    int loop_output;
 
    int flags;
#define AVFMT_FLAG_GENPTS       0x0001 ///< Generate missing pts even if it requires parsing future frames.
#define AVFMT_FLAG_IGNIDX       0x0002 ///< Ignore index.
#define AVFMT_FLAG_NONBLOCK     0x0004 ///< Do not block when reading packets from input.
#define AVFMT_FLAG_IGNDTS       0x0008 ///< Ignore DTS on frames that contain both DTS & PTS
#define AVFMT_FLAG_NOFILLIN     0x0010 ///< Do not infer any values from other values, just return what is stored in the container
#define AVFMT_FLAG_NOPARSE      0x0020 ///< Do not use AVParsers, you also must set AVFMT_FLAG_NOFILLIN as the fillin code works on frames and no parsing -> no frames. Also seeking to frames can not work if parsing to find frame boundaries has been disabled
#define AVFMT_FLAG_RTP_HINT     0x0040 ///< Add RTP hinting to the output file
 
    int loop_input;
    /** decoding: size of data to probe; encoding: unused. */
    unsigned int probesize;
 
    /**
     * Maximum time (in AV_TIME_BASE units) during which the input should
     * be analyzed in avformat_find_stream_info().
     */
    int max_analyze_duration;
 
    const uint8_t *key;
    int keylen;
 
    unsigned int nb_programs;
    AVProgram **programs;
 
    /**
     * Forced video codec_id.
     * Demuxing: Set by user.
     */
    enum CodecID video_codec_id;
    /**
     * Forced audio codec_id.
     * Demuxing: Set by user.
     */
    enum CodecID audio_codec_id;
    /**
     * Forced subtitle codec_id.
     * Demuxing: Set by user.
     */
    enum CodecID subtitle_codec_id;
 
    /**
     * Maximum amount of memory in bytes to use for the index of each stream.
     * If the index exceeds this size, entries will be discarded as
     * needed to maintain a smaller size. This can lead to slower or less
     * accurate seeking (depends on demuxer).
     * Demuxers for which a full in-memory index is mandatory will ignore
     * this.
     * muxing  : unused
     * demuxing: set by user
     */
    unsigned int max_index_size;
 
    /**
     * Maximum amount of memory in bytes to use for buffering frames
     * obtained from realtime capture devices.
     */
    unsigned int max_picture_buffer;
 
    unsigned int nb_chapters;
    AVChapter **chapters;
 
    /**
     * Flags to enable debugging.
     */
    int debug;
#define FF_FDEBUG_TS        0x0001
 
    /**
     * Raw packets from the demuxer, prior to parsing and decoding.
     * This buffer is used for buffering packets until the codec can
     * be identified, as parsing cannot be done without knowing the
     * codec.
     */
    struct AVPacketList *raw_packet_buffer;
    struct AVPacketList *raw_packet_buffer_end;
 
    struct AVPacketList *packet_buffer_end;
 
    AVMetadata *metadata;
 
    /**
     * Remaining size available for raw_packet_buffer, in bytes.
     * NOT PART OF PUBLIC API
     */
#define RAW_PACKET_BUFFER_SIZE 2500000
    int raw_packet_buffer_remaining_size;
 
    /**
     * Start time of the stream in real world time, in microseconds
     * since the unix epoch (00:00 1st January 1970). That is, pts=0
     * in the stream was captured at this real world time.
     * - encoding: Set by user.
     * - decoding: Unused.
     */
    int64_t start_time_realtime;
} AVFormatContext;

这是FFMpeg中最为基本的一个结构,是其他所有结构的根,是一个多媒体文件或流的根本抽象。其中:

  • nb_streams和streams所表示的AVStream结构指针数组包含了所有内嵌媒体流的描述;
  • iformat和oformat指向对应的demuxer和muxer指针;
  • pb则指向一个控制底层数据读写的ByteIOContext结构。
  • start_time和duration是从streams数组的各个AVStream中推断出的多媒体文件的起始时间和长度,以微妙为单位。

通常,这个结构由avformat_open_input在内部创建并以缺省值初始化部分成员。但是,如果调用者希望自己创建该结构,则需要显式为该结构的一些成员置缺省值——如果没有缺省值的话,会导致之后的动作产生异常。以下成员需要被关注:

  • probesize
  • mux_rate
  • packet_size
  • flags
  • max_analyze_duration
  • key
  • max_index_size
  • max_picture_buffer
  • max_delay

AVPacket

AVPacket定义在avcodec.h中,如下:

typedef struct AVPacket {
    /**
     * Presentation timestamp in AVStream->time_base units; the time at which
     * the decompressed packet will be presented to the user.
     * Can be AV_NOPTS_VALUE if it is not stored in the file.
     * pts MUST be larger or equal to dts as presentation cannot happen before
     * decompression, unless one wants to view hex dumps. Some formats misuse
     * the terms dts and pts/cts to mean something different. Such timestamps
     * must be converted to true pts/dts before they are stored in AVPacket.
     */
    int64_t pts;
    /**
     * Decompression timestamp in AVStream->time_base units; the time at which
     * the packet is decompressed.
     * Can be AV_NOPTS_VALUE if it is not stored in the file.
     */
    int64_t dts;
    uint8_t *data;
    int   size;
    int   stream_index;
    int   flags;
    /**
     * Duration of this packet in AVStream->time_base units, 0 if unknown.
     * Equals next_pts - this_pts in presentation order.
     */
    int   duration;
    void  (*destruct)(struct AVPacket *);
    void  *priv;
    int64_t pos;                            ///< byte position in stream, -1 if unknown
 
    /**
     * Time difference in AVStream->time_base units from the pts of this
     * packet to the point at which the output from the decoder has converged
     * independent from the availability of previous frames. That is, the
     * frames are virtually identical no matter if decoding started from
     * the very first frame or from this keyframe.
     * Is AV_NOPTS_VALUE if unknown.
     * This field is not the display duration of the current packet.
     *
     * The purpose of this field is to allow seeking in streams that have no
     * keyframes in the conventional sense. It corresponds to the
     * recovery point SEI in H.264 and match_time_delta in NUT. It is also
     * essential for some types of subtitle streams to ensure that all
     * subtitles are correctly displayed after seeking.
     */
    int64_t convergence_duration;
} AVPacket;

FFMPEG使用AVPacket来暂存媒体数据包及附加信息(解码时间戳、显示时间戳、时长等),这样的媒体数据包所承载的往往不是原始格式的音视频数据,而是以某种方式编码后的数据,编码信息由对应的媒体流结构AVStream给出。AVPacket包含以下数据域:

  • dts表示解码时间戳,pts表示显示时间戳,它们的单位是所属媒体流的时间基准。
  • stream_index给出所属媒体流的索引;
  • data为数据缓冲区指针,size为长度;
  • duration为数据的时长,也是以所属媒体流的时间基准为单位;
  • pos表示该数据在媒体流中的字节偏移量;
  • destruct为用于释放数据缓冲区的函数指针;
  • flags为标志域,其中,最低为置1表示该数据是一个关键帧。

AVPacket结构本身只是个容器,它使用data成员引用实际的数据缓冲区。这个缓冲区的管理方式有两种,其一是通过调用av_new_packet直接创建缓冲区,其二是引用已经存在的缓冲区。缓冲区的释放通过调用av_free_packet实现,其内部实现也采用了两种不同的释放方式,第一种方式是调用AVPacket的destruct函数,这个destruct函数可能是缺省的av_destruct_packet,对应av_new_packet或av_dup_packet创建的缓冲区,也可能是某个自定义的释放函数,表示缓冲区的提供者希望使用者在结束缓冲区的时候按照提供者期望的方式将其释放,第二种方式是仅仅将data和size的值清0,这种情况下往往是引用了一个已经存在的缓冲区,AVPacket的destruct指针为空。

在使用AVPacket时,对于缓冲区的提供者,必须注意通过设置destruct函数指针指定正确的释放方式,如果缓冲区提供者打算自己释放缓冲区,则切记将destruct置空;而对于缓冲区的使用者,务必在使用结束时调用av_free_packet释放缓冲区(虽然释放操作可能只是一个假动作)。如果某个使用者打算较长时间内占用一个AVPacket——比如不打算在函数返回之前释放它——最好调用av_dup_packet进行缓冲区的克隆,将其转化为自有分配的缓冲区,以免对缓冲区的不当占用造成异常错误。av_dup_packet会为destruct指针为空的AVPacket新建一个缓冲区,然后将原缓冲区的数据拷贝至新缓冲区,置data的值为新缓冲区的地址,同时设destruct指针为av_destruct_packet。

上述媒体结构可以通过FFMPEG提供的av_dump_format方法直观展示出来,以下例子以一个MPEG-TS文件为输入,其展示结果为:

Input #0, mpegts, from '/Videos/suite/ts/H.264_High_L3.1_720x480_23.976fps_AAC-LC.ts':
  Duration: 00:01:43.29, start: 599.958300, bitrate: 20934 kb/s


**收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。**
![img](https://img-blog.csdnimg.cn/img_convert/d1a04105fee8c4c7db7cc102891c40a7.png)
![img](https://img-blog.csdnimg.cn/img_convert/332d0b24d30ffeedb1c24ae01220001e.png)

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**

**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

acket,对应av\_new\_packet或av\_dup\_packet创建的缓冲区,也可能是某个自定义的释放函数,表示缓冲区的提供者希望使用者在结束缓冲区的时候按照提供者期望的方式将其释放,第二种方式是仅仅将data和size的值清0,这种情况下往往是引用了一个已经存在的缓冲区,AVPacket的destruct指针为空。


在使用AVPacket时,对于缓冲区的提供者,必须注意通过设置destruct函数指针指定正确的释放方式,如果缓冲区提供者打算自己释放缓冲区,则切记将destruct置空;而对于缓冲区的使用者,务必在使用结束时调用av\_free\_packet释放缓冲区(虽然释放操作可能只是一个假动作)。如果某个使用者打算较长时间内占用一个AVPacket——比如不打算在函数返回之前释放它——最好调用av\_dup\_packet进行缓冲区的克隆,将其转化为自有分配的缓冲区,以免对缓冲区的不当占用造成异常错误。av\_dup\_packet会为destruct指针为空的AVPacket新建一个缓冲区,然后将原缓冲区的数据拷贝至新缓冲区,置data的值为新缓冲区的地址,同时设destruct指针为av\_destruct\_packet。


上述媒体结构可以通过FFMPEG提供的av\_dump\_format方法直观展示出来,以下例子以一个MPEG-TS文件为输入,其展示结果为:



Input #0, mpegts, from ‘/Videos/suite/ts/H.264_High_L3.1_720x480_23.976fps_AAC-LC.ts’:
Duration: 00:01:43.29, start: 599.958300, bitrate: 20934 kb/s

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
[外链图片转存中…(img-d7hYBY7q-1715872757123)]
[外链图片转存中…(img-58NzlDEY-1715872757124)]

如果你需要这些资料,可以戳这里获取

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 26
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ffmpeg是一个非常强大的多媒体处理工具,通过一系列的命令可以完成各种音视频处理任务。以下是对ffmpeg命令的详解: 1. 合成视频:可以将多个图片序列帧和音频文件合成为视频文件。例如,可以使用以下命令将darkdoor.[001-100].jpg序列帧和001.mp3音频文件合成为视频文件darkdoor.avi: ffmpeg -i 001.mp3 -i darkdoor.%d.jpg -s 1024x768 -author skypp -vcodec mpeg4 darkdoor.avi 2. 导出视频:可以将视频文件导出为其他格式,如mov格式。例如,可以使用以下命令将darkdoor.avi导出为darkdoor.mov: ffmpeg -i darkdoor.avi darkdoor.mov 3. 导出图片序列帧:可以将视频文件导出为一系列的图片序列帧。例如,可以使用以下命令将bc-cinematic-en.avi导出为example.%d.jpg序列帧: ffmpeg -i bc-cinematic-en.avi example.%d.jpg 4. 安装ffmpeg:在Debian系统下,可以使用以下命令安装ffmpeg: apt-get install ffmpeg 综上所述,ffmpeg是一个功能强大的多媒体处理工具,可以完成音视频剪切、转码、滤镜、拼接、混音、截图等任务。详细的使用方法可以参考FFmpeg官方文档[4]。 引用: ffmpeg命令详解 ffmpeg非常强大,轻松几条命令就可以完成你的工作 在cmd中先将目录切换到ffmpeg.exe对应的文件目录 FFmpeg多媒体库支持的命令行调用分为三个模块:ffmpeg、ffprobe、ffplay。其中ffmpeg命令行常用于音视频剪切、转码、滤镜、拼接、混音、截图等;ffprobe用于检测多媒体流格式;ffplay用于播放视频。详情可查阅FFmpeg官方文档:ffmpeg Documentation。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值