3.2 AVStream

3.2.1 对比

AVPacketAVStreamFFmpeg 中两个不同的结构体,别离用于处理音视频数据的帧和表明音视频流的信息。它们在功能和用途上有不同的规划和定位,因此在运用时有各自的优势。

  1. AVPacket:

    • AVPacket 结构体用于存储编解码器发生的紧缩后的音视频数据,即紧缩后的帧。它包含了紧缩数据的指针、大小、时刻戳等信息。
    • 首要用于在音视频的编码和解码过程中传递数据。在解封装(Demuxing)阶段,你会运用 av_read_frame 从文件或网络流中读取 AVPacket
    • 在封装(Muxing)阶段,你将经过编码器得到的 AVFrame 编码为 AVPacket,再写入文件。
  2. AVStream:

    • AVStream 结构体代表了媒体文件中的一个音视频流,包含了该流的详细信息,比方编解码器的参数、时基信息等。
    • 首要用于表明文件中的媒体流的特点,在文件的翻开和读取阶段,你能够经过 AVStream 获取有关音视频流的信息,例如编码器类型、帧率、分辨率等。
    • 在解封装阶段,AVStream 用于表明文件中的不同音视频流,你能够根据需要处理这些流。

简而言之,AVPacket 首要用于处理音视频数据的帧,而 AVStream 首要用于表明音视频流的信息。它们别离服务于不同的层面,AVPacket 在数据处理阶段更为重要,而 AVStream 在文件的元数据表明和读取阶段更为要害。

尽管你能够运用 AVStream 来间接访问 AVPacket,但直接运用 AVPacket 更直观,因为它包含了处理帧数据所需的所有信息。在处理音视频数据的过程中,通常会频繁运用 AVPacket 来传递、处理和释放数据。

3.2.2 特点

AVStream 功能
int index 流在avformatContext的索引序号,创建stream时分,由ffmpeg库自动分配,无需手动设置
id
AVCodecContext *codec
void *priv_data
AVRational time_base 时刻基
int64_t duration 流总长时刻戳
nt64_t nb_frames
AVCodecParameters *codecpar 编码器参数
AVStream->codecpar 功能
AVMEDIA_TYPE_AUDIO == in_stream->codecpar->codec_type 音频
sample_rate 音编码器采样率,Hz
format (AV_SAMPLE_FMT_FLTP/ AV_SAMPLE_FMT_S16P 音频采样格局
channels 音频信道数目
codec_id (AV_CODEC_ID_AAC AV_CODEC_ID_MP3 音频紧缩编码格局
profile 用于配置adts头部信息
AVMEDIA_TYPE_VIDEO == in_stream->codecpar->codec_type 视频
codec_id (AV_CODEC_ID_MPEG4 AV_CODEC_ID_h264 视频紧缩编码格局
width height 视频宽高
  • codec_type类型

    enum AVMediaType {
      AVMEDIA_TYPE_UNKNOWN = -1, ///< 错误
      AVMEDIA_TYPE_VIDEO,
      AVMEDIA_TYPE_AUDIO,
      AVMEDIA_TYPE_DATA,     ///< Opaque data information usually continuous
      AVMEDIA_TYPE_SUBTITLE,   /// 字母
      AVMEDIA_TYPE_ATTACHMENT,  /// 音频专辑封面图
      AVMEDIA_TYPE_NB
    };
    ​
    
  • codec_id类型

    // 音视频编码器类型
    enum AVCodecID {
      AV_CODEC_ID_NONE,
    ​
      /* video codecs */
      AV_CODEC_ID_MPEG1VIDEO,
      AV_CODEC_ID_MPEG2VIDEO, ///< preferred ID for MPEG-1/2 video decoding
      AV_CODEC_ID_H261,
      AV_CODEC_ID_H263,
      AV_CODEC_ID_RV10,
       .
       .
       .
    
  • format类型

    // 音频采样格局
    enum AVSampleFormat {
      AV_SAMPLE_FMT_NONE = -1,
      AV_SAMPLE_FMT_U8,     ///< unsigned 8 bits
      AV_SAMPLE_FMT_S16,     ///< signed 16 bits
      AV_SAMPLE_FMT_S32,     ///< signed 32 bits
      AV_SAMPLE_FMT_FLT,     ///< float
      AV_SAMPLE_FMT_DBL,     ///< doubleAV_SAMPLE_FMT_U8P,     ///< unsigned 8 bits, planar
      AV_SAMPLE_FMT_S16P,    ///< signed 16 bits, planar
      AV_SAMPLE_FMT_S32P,    ///< signed 32 bits, planar
      AV_SAMPLE_FMT_FLTP,    ///< float, planar
      AV_SAMPLE_FMT_DBLP,    ///< double, planar
      AV_SAMPLE_FMT_S64,     ///< signed 64 bits
      AV_SAMPLE_FMT_S64P,    ///< signed 64 bits, planarAV_SAMPLE_FMT_NB      ///< Number of sample formats. DO NOT USE if linking dynamically
    };
    

3.2.3 接口

// 创建流
AVStream *st = avformat_new_stream(fmt_ctx_, NULL);
// 从编码器复制参数信息
avcodec_parameters_from_context(st->codecpar, codec_ctx);
av_dump_format(fmt_ctx_, 0, url_.c_str(), 1);

3.2.4 示例

关于打印stream时刻的详细解释

#include <stdio.h>
#include <libavformat/avformat.h>
​
​
int main(int argc, char **argv)
{
  //翻开网络流。这儿假如只需要读取本地媒体文件,不需要用到网络功能,能够不用加上这一句
//   avformat_network_init();
​
  const char *default_filename = "believe.mp4";
​
  char *in_filename = NULL;
​
  if(argv[1] == NULL)
   {
    in_filename = default_filename;
   }
  else
   {
    in_filename = argv[1];
   }
  printf("in_filename = %sn", in_filename);
​
  //AVFormatContext是描述一个媒体文件或媒体流的构成和基本信息的结构体
  AVFormatContext *ifmt_ctx = NULL;      // 输入文件的demux
​
  int videoindex = -1;    // 视频索引
  int audioindex = -1;    // 音频索引
​
​
  // 翻开文件,首要是探测协议类型,假如是网络文件则创建网络链接
  int ret = avformat_open_input(&ifmt_ctx, in_filename, NULL, NULL);
  if (ret < 0) //假如翻开媒体文件失利,打印失利原因
   {
    char buf[1024] = { 0 };
    av_strerror(ret, buf, sizeof(buf) - 1);
    printf("open %s failed:%sn", in_filename, buf);
    goto failed;
   }
​
  // mp4不需要,flv需要?????
  ret = avformat_find_stream_info(ifmt_ctx, NULL);
  if (ret < 0) //假如翻开媒体文件失利,打印失利原因
   {
    char buf[1024] = { 0 };
    av_strerror(ret, buf, sizeof(buf) - 1);
    printf("avformat_find_stream_info %s failed:%sn", in_filename, buf);
    goto failed;
   }
​
  //翻开媒体文件成功
  printf_s("n==== av_dump_format in_filename:%s ===n", in_filename);
  
  av_dump_format(ifmt_ctx, 0, in_filename, 0);
  printf_s("n==== av_dump_format finish =======nn");
  
  // url: 调用avformat_open_input读取到的媒体文件的途径/名字
  printf("media name:%sn", ifmt_ctx->url);
  
  // nb_streams: nb_streams媒体流数量
  printf("stream number:%dn", ifmt_ctx->nb_streams);
  // bit_rate: 媒体文件的码率,单位为bps
  printf("media average ratio:%lldkbpsn",(int64_t)(ifmt_ctx->bit_rate/1024));
  // 时刻
  int total_seconds, hour, minute, second;
  // duration: 媒体文件时长,单位奇妙
  total_seconds = (ifmt_ctx->duration) / AV_TIME_BASE; // 1000us = 1ms, 1000ms = 1秒
  hour = total_seconds / 3600;
  minute = (total_seconds % 3600) / 60;
  second = (total_seconds % 60);
  //经过上述运算,能够得到媒体文件的总时长
  printf("total duration: %02d:%02d:%02dn", hour, minute, second);
  printf("n");
  /*
   * 老版别经过遍历的方式读取媒体文件视频和音频的信息
   * 新版别的FFmpeg新增加了函数av_find_best_stream,也能够取得相同的作用
   */
  for (uint32_t i = 0; i < ifmt_ctx->nb_streams; i  )
   {
    AVStream *in_stream = ifmt_ctx->streams[i];// 音频流、视频流、字幕流
    //假如是音频流,则打印音频的信息
    if (AVMEDIA_TYPE_AUDIO == in_stream->codecpar->codec_type)
     {
      printf("----- Audio info:n");
      // index: 每个流成分在ffmpeg解复用剖析后都有仅有的index作为标识
      printf("index:%dn", in_stream->index);
      // sample_rate: 音频编解码器的采样率,单位为Hz
      printf("samplerate:%dHzn", in_stream->codecpar->sample_rate);
      // codecpar->format: 音频采样格局
      if (AV_SAMPLE_FMT_FLTP == in_stream->codecpar->format)
       {
        printf("sampleformat:AV_SAMPLE_FMT_FLTPn");
       }
      else if (AV_SAMPLE_FMT_S16P == in_stream->codecpar->format)
       {
        printf("sampleformat:AV_SAMPLE_FMT_S16Pn");
       }
      // channels: 音频信道数目
      printf("channel number:%dn", in_stream->codecpar->channels);
      // codec_id: 音频紧缩编码格局
      if (AV_CODEC_ID_AAC == in_stream->codecpar->codec_id)
       {
        printf("audio codec:AACn");
       }
      else if (AV_CODEC_ID_MP3 == in_stream->codecpar->codec_id)
       {
        printf("audio codec:MP3n");
       }
      else
       {
        printf("audio codec_id:%dn", in_stream->codecpar->codec_id);
       }
      // 音频总时长,单位为秒。留意假如把单位放大为毫秒或者奇妙,音频总时长跟视频总时长不一定持平的
      if(in_stream->duration != AV_NOPTS_VALUE)
       {
        int duration_audio = (in_stream->duration) * av_q2d(in_stream->time_base);
        //将音频总时长转化为时分秒的格局打印到操控台上
        printf("audio duration: %02d:%02d:%02dn",
            duration_audio / 3600, (duration_audio % 3600) / 60, (duration_audio % 60));
       }
      else
       {
        printf("audio duration unknown");
       }
​
      printf("n");
​
      audioindex = i; // 获取音频的索引
     }
    else if (AVMEDIA_TYPE_VIDEO == in_stream->codecpar->codec_type) //假如是视频流,则打印视频的信息
     {
      printf("----- Video info:n");
      printf("index:%dn", in_stream->index);
      // avg_frame_rate: 视频帧率,单位为fps,表明每秒呈现多少帧
      printf("fps:%lffpsn", av_q2d(in_stream->avg_frame_rate));
      if (AV_CODEC_ID_MPEG4 == in_stream->codecpar->codec_id) //视频紧缩编码格局
       {
        printf("video codec:MPEG4n");
       }
      else if (AV_CODEC_ID_H264 == in_stream->codecpar->codec_id) //视频紧缩编码格局
       {
        printf("video codec:H264n");
       }
      else
       {
        printf("video codec_id:%dn", in_stream->codecpar->codec_id);
       }
      // 视频帧宽度和帧高度
      printf("width:%d height:%dn", in_stream->codecpar->width,
          in_stream->codecpar->height);
      //视频总时长,单位为秒。留意假如把单位放大为毫秒或者奇妙,音频总时长跟视频总时长不一定持平的
      if(in_stream->duration != AV_NOPTS_VALUE)
       {
        int duration_video = (in_stream->duration) * av_q2d(in_stream->time_base);
        printf("video duration: %02d:%02d:%02dn",
            duration_video / 3600,
            (duration_video % 3600) / 60,
            (duration_video % 60)); //将视频总时长转化为时分秒的格局打印到操控台上
       }
      else
       {
        printf("video duration unknown");
       }
​
      printf("n");
      videoindex = i;
     }
   }
​
  AVPacket *pkt = av_packet_alloc();
​
  int pkt_count = 0;
  int print_max_count = 10;
  printf("n-----av_read_frame startn");
  while (1)
   {
    ret = av_read_frame(ifmt_ctx, pkt);
    if (ret < 0)
     {
      printf("av_read_frame endn");
      break;
     }
​
    if(pkt_count   < print_max_count)
     {
      if (pkt->stream_index == audioindex)
       {
        printf("audio pts: %lldn", pkt->pts);
        printf("audio dts: %lldn", pkt->dts);
        printf("audio size: %dn", pkt->size);
        printf("audio pos: %lldn", pkt->pos);
        printf("audio duration: %lfnn",
            pkt->duration * av_q2d(ifmt_ctx->streams[audioindex]->time_base));
       }
      else if (pkt->stream_index == videoindex)
       {
        printf("video pts: %lldn", pkt->pts);
        printf("video dts: %lldn", pkt->dts);
        printf("video size: %dn", pkt->size);
        printf("video pos: %lldn", pkt->pos);
        printf("video duration: %lfnn",
            pkt->duration * av_q2d(ifmt_ctx->streams[videoindex]->time_base));
       }
      else
       {
        printf("unknown stream_index:n", pkt->stream_index);
       }
     }
​
    av_packet_unref(pkt);
   }
​
  if(pkt)
    av_packet_free(&pkt);
failed:
  if(ifmt_ctx)
    avformat_close_input(&ifmt_ctx);
​
​
  getchar(); //加上这一句,防止程序打印完信息马上退出
  return 0;
}
​