音视频播放器—初始化 *** 作

音视频播放器—初始化 *** 作,第1张

------------------------------------全系列文章目录------------------------------------

播放器类的项目初始化大同小异,所以本文章将作为所有播放器类项目初始化 *** 作的记录。

大致流程
  • 注册FFmpeg相关的组件。
  • 打开输入的媒体流文件,通过判断输入文件名后缀或读取头部信息,检测输入流的协议信息和封装格式信息。
  • 读取媒体流的数据包来获取并赋值流(AVStream)的信息。
  • 找到并打开匹配且已注册的解码器。
av_register_all()
  • 该函数调用avcodec_register_all()注册了和编解码器有关的组件,如硬件加速、解码器、编码器、Parser和Bitstream Filter;同时注册了复用器、解复用器和协议处理器。

  • 注册函数都大同小异,都是设定一个全局静态的链表头,将注册的组件以链表的形式保存起来,同时进行一些初始化 *** 作。

  • 如编解码器的注册函数如下,可知其最终是调用了avcodec_register这个函数。

    #define REGISTER_ENCODER(X,x) { \
              extern AVCodec ff_##x##_encoder; \
              if(CONFIG_##X##_ENCODER)  avcodec_register(&ff_##x##_encoder); }
    #define REGISTER_DECODER(X,x) { \
              extern AVCodec ff_##x##_decoder; \
              if(CONFIG_##X##_DECODER)  avcodec_register(&ff_##x##_decoder); }
    #define REGISTER_ENCDEC(X,x)  REGISTER_ENCODER(X,x); REGISTER_DECODER(X,x)
    /* 
    	如REGISTER_ENCDEC(MPEG4, mpeg4)即
    	extern AVCodec ff_mpeg4_encoder;
        if(CONFIG_MPEG4_ENCODER)  avcodec_register(&ff_mpeg4_encoder);
        extern AVCodec ff_mpeg4_decoder;
        if(CONFIG_MPEG4_DECODER)  avcodec_register(&ff_mpeg4_decoder);
    */
    
    • avcodec_register函数如下,其中first_avcodec是全局静态变量,该注册函数即是把所有的编解码器都以链表形式,保存在以first_avcodec为链表头的链表中,同时进行一些初始化 *** 作。

      static AVCodec *first_avcodec = NULL;
      
      void avcodec_register(AVCodec *codec)
      {
          AVCodec **p;
          avcodec_init();
          p = &first_avcodec;
          while (*p != NULL) p = &(*p)->next;
          *p = codec;
          codec->next = NULL;
          if (codec->init_static_data)
              codec->init_static_data(codec);
      }
      
avformat_open_input()
  • 该函数用于打开一个输入的媒体流,同时通过判断输入文件名后缀或读取头部信息,检测输入流的协议信息和封装格式信息;并初始化管理输入/输出数据流的结构体AVIOContext、初始化输入格式结构体AVInputFormat。

  • int avformat_open_input(AVFormatContext ** ps, const char *url, const AVInputFormat *fmt, AVDictionary **options)

    • ps:用户提供的AVFormatContext结构体,调用成功后对其进行处理,若为NULL,则会自动使用avformat_alloc_context申请空间。
    • url:输入的媒体流名字。
    • fmt:用于强制使用该特定的输入格式,若为NULL,则会自动检测格式。
    • options:附加的一些选项。
avformat_find_stream_info
  • 该函数通过读取媒体流的数据包来获取并赋值流(AVStream)的信息,中途包括查找并打开解码器、读取数据包并解码等 *** 作。
  • int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
    • ic:描述媒体流的结构体AVFormatContext。
    • options:附加的一些选项。
avcodec_find_decoder
  • 该函数根据编解码器id,找到匹配且已注册的解码器;注册 *** 作发生在av_register_all的avcodec_register中,即是从以first_avcodec为链表头的链表中,找寻匹配的解码器。
  • const AVCodec* avcodec_find_decoder(enum AVCodecID id)
    • id:编解码器id,如AV_CODEC_ID_MJPEG,AV_CODEC_ID_H264等。
avcodec_open2
  • 该函数用于初始化编解码器上下文AVCodecContext和相应解码器AVCodec ,以使用相应解码器进行解码 *** 作。
  • int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)
    • avctx:需要初始化的编解码器上下文AVCodecContext。
    • codec:需要打开的解码器AVCodec 。
    • options:附加的一些配置参数。
使用示例
  • 定义如下数据类型

    /*解码器*/
    class Decode {
    public:
    	AVCodecContext		*codec_ctx; //描述编解码器相关信息
    	AVCodec				*codec;		//描述编解码器
    	......
    	int codec_config(AVCodecContext *ctx, enum AVMediaType _type);	/*配置编解码器*/
    private:
    	enum AVMediaType	type;
        ......
    };
    
    /*音频流*/
    class AudioCtrl {
    public:
    	class Decode		dec;
    	......
    private:
    };
    
    /*媒体流*/
    class MediaCtrl {
    public:
    	AVFormatContext		*fmt_ctx;
    	AVStream			*video_st, *audio_st;
    	int					audio_index, video_index;
    	class AudioCtrl1	au;
    	MediaCtrl(char *_media);
    	~MediaCtrl();
        ......
    private:
    	char				*media_name;
        ......
    };
    
  • 初始化 *** 作

    MediaCtrl::MediaCtrl(char *_media)
    {
    	int ret;
    	AVCodecContext *codec_ctx =  NULL;
    	media_name = _media;
    	
    	/*注册ffmpeg组件,初始化SDL,同时完成相关初始化 *** 作*/
    	av_register_all();													//注册所有ffmpeg组件
    	if (avformat_network_init()) {										/*初始化音视频格式网络连接*/	
    		printf("init avformat error !\n");
    	}
    	if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {	/*初始化SDL - 音视频和定时器*/
    		printf( "Could not initialize SDL - %s\n", SDL_GetError()); 
    	}
    
    	fmt_ctx = avformat_alloc_context();									//申请AVFormatContext空间
    	if (fmt_ctx == NULL) {
    		printf("alloc avformat error !\n");
    	}
    	
    	ret = avformat_open_input(&fmt_ctx, media_name, NULL, NULL);		//打开音视频文件
    	if (ret != 0) {
    		printf("can not open input AV file !\n");
    	}
    
    	/*查找数据流信息,并从编码器信息中找到音频流信息*/
    	ret = avformat_find_stream_info(fmt_ctx, NULL);					//查找数据流信息
    	if (ret != 0) {								
    		printf("can not get input AV stream info !\n");
    	}
    	audio_index = -1;
    	for (int i = 0; i < fmt_ctx->nb_streams; i ++) {
    		if (fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audio_index == -1) {
    			audio_index = i;
    		} else if (fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && video_index == -1) {
    			video_index = i;
    		}
    	}
    
    	codec_ctx = fmt_ctx->streams[audio_index]->codec;
    	ret = au.dec.codec_config(codec_ctx, AVMEDIA_TYPE_AUDIO);		//配置音频解码器
    	if (ret != 0) {
    		printf("audio decoder config error\n");
    	}
    }
    
    int Decode::codec_config(AVCodecContext *ctx, enum AVMediaType _type)
    {
    	int ret;
    	codec_ctx	= ctx;
      	type        = _type;
      	
      	switch (type) {
      	case AVMEDIA_TYPE_AUDIO:
    		codec		= avcodec_find_decoder(ctx->codec_id);	//获取音频流解码器
    		if (codec == NULL) {
    			printf("can not find the audio codec !\n");
    			return -1;
    		}
    		ret = avcodec_open2(codec_ctx, codec, NULL);		//打开音频流解码器
    		if (ret != 0) {
    			printf("can not open the audio codec !\n");
    			return -1;
    		}
    		break;
    	case AVMEDIA_TYPE_VIDEO:
    		break;
    	default:
    		break;
    	}
    	
    	return 0;
    }
    

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/725019.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)

保存