網上關于這個結構的介紹有很多,我在之前最初學習時檢視過相關源碼記錄了一些筆記,由于更換固态硬碟筆記一直在原來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的形式傳回到調用出在外部,在外部可以進行放入隊列或者是重采樣編碼等操作