天天看點

AVFormatContext的初始化

網上關于這個結構的介紹有很多,我在之前最初學習時檢視過相關源碼記錄了一些筆記,由于更換固态硬碟筆記一直在原來128g的固态中,想将其改成U盤成本實在是太高,很多筆記都扔在裡面了,真的好蛋疼,我直接寫一些我的使用,其他的在一點一點完善

AVFormatContext* get_input_avformatcontex_f(char *input_url){
    int ret = 0;
    char errbuf[1024] = {0,};
    AVFormatContext *fmt_ctx = NULL;
    AVInputFormat *ifmt = NULL;
    AVDictionary *options = NULL;
    ifmt = av_find_input_format("avfoundation");
    //檢視裝置支援什麼作為輸入
    av_dict_set(&options,"list_devices","true",0);
    char *input_stream_url = input_url; //設定輸入源通常為本地檔案或一些連結
    //如果是使用ffmpeg進行采集則需要設定一些音頻或視訊的采集參數 
    /*
     av_dict_set(&options, "video_size", w_h_c_str, 0);
     av_dict_set(&options, "framerate", "30", 0);
     av_dict_set(&options, "pixel_format", "nv12", 0);
        {"pixel_format", "set pixel format", OFFSET(pix_fmt), AV_OPT_TYPE_PIXEL_FMT, {.i64=AV_PIX_FMT_NONE}, -1, INT_MAX, 0 },
        {"video_size", "set video size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str=NULL}, 0, INT_MAX, 0 }, {NULL}, };
    */
    av_log_set_level(AV_LOG_DEBUG);
    if ((ret = avformat_open_input(&fmt_ctx, input_stream_url, ifmt, &options)) < 0){
        av_strerror(ret, errbuf, 1024);
        fprintf(stderr, "Failed to open audio device, [%d]%s\n", ret, errbuf);
        return NULL;
    }

    return fmt_ctx;
}
           

然後建立一個AVPacket用來接收封裝資料

AVPacket* get_avpacket_f_input(){
    AVPacket *av_packet = NULL;
    av_packet = av_packet_alloc();
    if (!av_packet) {
        fprintf(stderr,"av_packet_alloc failed");
        goto __ERROR;
    }
    return av_packet;
__ERROR:
    if (!av_packet) {
        av_packet_free(&av_packet);
    }
    return NULL;
}
           

在打開輸入上下文之後可以對擷取資料進行解碼

-(void)decode_audio:(AVCodecContext *)input_ctx and_packet:(AVPacket *)input_avpacket{
    int ret = -1;
    ret = avcodec_send_packet(input_ctx, input_avpacket);
    while (ret >=0 ) {
        ret = avcodec_receive_frame(input_ctx,input_a_frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            return;
        else if (ret < 0) {
            fprintf(stderr, "Error during decoding\n");
            exit(1);
        }
        if (ret >= 0) {          
            self.callback(Collect_Data_Audio, input_a_frame);
        }
        av_packet_unref(input_packet);//一定要記得釋放 如果不釋放記憶體會暴漲
    }
}
           

可以在擷取資料之前檢視輸入流的相關資訊代碼如下

- (void)get_input_stream_oc:(char *)input_url and_avFormatContext:(AVFormatContext *)input_fmt{
    avformat_find_stream_info(input_fmt, NULL);
    av_dump_format(input_fmt, 0, input_url , 0);
    for (int i = 0; i < input_fmt->nb_streams; i++) {
         AVStream  *stream = input_fmt->streams[i];
        if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {//列印流資訊
            audio_stream_index = i;
        }else if(stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
            video_stream_index = i;
        }
    }
    //音頻
    AVCodec *acodec = avcodec_find_decoder(input_fmt->streams[audio_stream_index]->codecpar->codec_id);
    AVCodecContext *ac = avcodec_alloc_context3(acodec);
    ac->sample_rate = input_fmt->streams[audio_stream_index]->codecpar->sample_rate;
    ac->sample_fmt = input_fmt->streams[audio_stream_index]->codecpar->format;
    ac->channels = input_fmt->streams[audio_stream_index]->codecpar->channels;
    avcodec_open2(ac, acodec, NULL);
    
    AVFrame *frame = av_frame_alloc();
    frame->nb_samples = ac->frame_size;
    frame->channel_layout = ac->channel_layout;
    frame->format = ac->sample_fmt;
    
    av_frame_get_buffer(frame, 0);
    input_de_codec_a = acodec;
    input_de_ctx_a = ac;
    input_a_frame = frame;
    //視訊
    AVCodecContext* vc = input_fmt->streams[video_stream_index]->codec;
    AVCodec* vcodec = avcodec_find_decoder(vc->codec_id);
    avcodec_open2(vc, vcodec, NULL);
    AVFrame *vframe = av_frame_alloc();
    av_frame_get_buffer(vframe, 0);
    input_de_codec_v = vcodec;
    input_de_ctx_v = vc;
    input_v_frame = vframe; //c函數内無法這樣給成員變量指派 應該是文法的問題
    [self config_av_inputfmt:fmt_ctx and_videoindex:video_stream_index and_audioindex:audio_stream_index];
}
           

我将采集到的裸資料以call的形式傳回到調用出在外部,在外部可以進行放入隊列或者是重采樣編碼等操作