transcode_init()

ffmpeg版本是3.1.3
此函数是为转码做初始化。设置编码参数,打开所有输出流的编码器,打开所有输入流的解码器,写入所有输出文件的文件头,具体看注释

static int transcode_init(void)
{int ret = 0, i, j, k;AVFormatContext *oc;OutputStream *ost;InputStream *ist;char error[1024] = {0};int want_sdp = 1;//大致先解释一下,不一定对。/*此处是处理在命令行中设置的滤镜,最终目的是对ofilter->ost->source_index进行设置也就是对输出流针对的输入流设置*/for (i = 0; i < nb_filtergraphs; i++) {FilterGraph *fg = filtergraphs[i];//循环所有输出流for (j = 0; j < fg->nb_outputs; j++) {OutputFilter *ofilter = fg->outputs[j];//如果已经有值就看下一个输出流if (!ofilter->ost || ofilter->ost->source_index >= 0)continue;if (fg->nb_inputs != 1)continue;for (k = nb_input_streams-1; k >= 0 ; k--)if (fg->inputs[0]->ist == input_streams[k])break;ofilter->ost->source_index = k;}}/* init framerate emulation *//*对帧率仿真做初始化,循环所有输入文件,和所有输入流*/for (i = 0; i < nb_input_files; i++) {InputFile *ifile = input_files[i];if (ifile->rate_emu)//ifile->ist_index是本文件的流在input_streams中的第一个流的下标,在open_input_file里面设置for (j = 0; j < ifile->nb_streams; j++)//设置此流的开始时间input_streams[j + ifile->ist_index]->start = av_gettime_relative();}/* for each output stream, we compute the right encoding parameters */for (i = 0; i < nb_output_streams; i++) {AVCodecContext *enc_ctx;AVCodecContext *dec_ctx = NULL;ost = output_streams[i];oc  = output_files[ost->file_index]->ctx;//从input_streams中将输出流对应的输入流找到,对应关系在new_output_stream中传参设置ist = get_input_stream(ost);//如果输出是attachment类型就不处理。这个还不是很清楚。if (ost->attachment_filename)continue;//看是否流拷贝对enc_ctx赋值不同的编码器enc_ctx = ost->stream_copy ? ost->st->codec : ost->enc_ctx;if (ist) {//找到了对应的输入流,对一些参数进行拷贝dec_ctx = ist->dec_ctx;ost->st->disposition          = ist->st->disposition;//对应参数bits_per_raw_sample,在new_video_stream()中设置enc_ctx->bits_per_raw_sample    = dec_ctx->bits_per_raw_sample;enc_ctx->chroma_sample_location = dec_ctx->chroma_sample_location;} else {for (j=0; j<oc->nb_streams; j++) {AVStream *st = oc->streams[j];if (st != ost->st && st->codec->codec_type == enc_ctx->codec_type)break;}if (j == oc->nb_streams)if (enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO || enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO)ost->st->disposition = AV_DISPOSITION_DEFAULT;}//如果只是复制一个流(不用解码后再编码),则把输入流的编码参数直接复制给输出流    //此时是不需要解码也不需要编码的,所以不需打开解码器和编码器if (ost->stream_copy) {AVRational sar;uint64_t extra_size;av_assert0(ist && !ost->filter);//计算输出流的编解码器的extradata的大小,然后分配容纳extradata的缓冲//加一个AV_INPUT_BUFFER_PADDING_SIZE的大小,好像是因为四字节对齐的读入方式,防止读多了extra_size = (uint64_t)dec_ctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE;if (extra_size > INT_MAX) {return AVERROR(EINVAL);}/* if stream_copy is selected, no need to decode or encode */enc_ctx->codec_id   = dec_ctx->codec_id;//编码方式h264,mpeg4enc_ctx->codec_type = dec_ctx->codec_type;//编码类型:视频/音频//对应参数"tag"if (!enc_ctx->codec_tag) {unsigned int codec_tag;if (!oc->oformat->codec_tag ||av_codec_get_id (oc->oformat->codec_tag, dec_ctx->codec_tag) == enc_ctx->codec_id ||!av_codec_get_tag2(oc->oformat->codec_tag, dec_ctx->codec_id, &codec_tag))enc_ctx->codec_tag = dec_ctx->codec_tag;}enc_ctx->bit_rate       = dec_ctx->bit_rate;//平均码率enc_ctx->rc_max_rate    = dec_ctx->rc_max_rate;//最大码率,rc_min_rate为最小码率enc_ctx->rc_buffer_size = dec_ctx->rc_buffer_size;//decoder bitstream buffer sizeenc_ctx->field_order    = dec_ctx->field_order;//场序,顶场和底场谁先编码谁先显示的问题。不是很懂if (dec_ctx->extradata_size) {enc_ctx->extradata      = av_mallocz(extra_size);if (!enc_ctx->extradata) {return AVERROR(ENOMEM);}//然后把输入流的编解码器的extradata复制到输出流的编解码器中memcpy(enc_ctx->extradata, dec_ctx->extradata, dec_ctx->extradata_size);}enc_ctx->extradata_size= dec_ctx->extradata_size;enc_ctx->bits_per_coded_sample  = dec_ctx->bits_per_coded_sample;//帧率enc_ctx->time_base = ist->st->time_base;/** Avi is a special case here because it supports variable fps but* having the fps and timebase differe significantly adds quite some* overhead*///copy_tb对应参数"copytb","copy input stream time base when stream copying"if(!strcmp(oc->oformat->name, "avi")) {if ( copy_tb<0 && ist->st->r_frame_rate.num&& av_q2d(ist->st->r_frame_rate) >= av_q2d(ist->st->avg_frame_rate)&& 0.5/av_q2d(ist->st->r_frame_rate) > av_q2d(ist->st->time_base)&& 0.5/av_q2d(ist->st->r_frame_rate) > av_q2d(dec_ctx->time_base)&& av_q2d(ist->st->time_base) < 1.0/500 && av_q2d(dec_ctx->time_base) < 1.0/500|| copy_tb==2){enc_ctx->time_base.num = ist->st->r_frame_rate.den;enc_ctx->time_base.den = 2*ist->st->r_frame_rate.num;enc_ctx->ticks_per_frame = 2;} else if (   copy_tb<0 && av_q2d(dec_ctx->time_base)*dec_ctx->ticks_per_frame > 2*av_q2d(ist->st->time_base)&& av_q2d(ist->st->time_base) < 1.0/500|| copy_tb==0){enc_ctx->time_base = dec_ctx->time_base;enc_ctx->time_base.num *= dec_ctx->ticks_per_frame;enc_ctx->time_base.den *= 2;enc_ctx->ticks_per_frame = 2;}} else if(!(oc->oformat->flags & AVFMT_VARIABLE_FPS)&& strcmp(oc->oformat->name, "mov") && strcmp(oc->oformat->name, "mp4") && strcmp(oc->oformat->name, "3gp")&& strcmp(oc->oformat->name, "3g2") && strcmp(oc->oformat->name, "psp") && strcmp(oc->oformat->name, "ipod")&& strcmp(oc->oformat->name, "f4v")) {if(   copy_tb<0 && dec_ctx->time_base.den&& av_q2d(dec_ctx->time_base)*dec_ctx->ticks_per_frame > av_q2d(ist->st->time_base)&& av_q2d(ist->st->time_base) < 1.0/500|| copy_tb==0){enc_ctx->time_base = dec_ctx->time_base;enc_ctx->time_base.num *= dec_ctx->ticks_per_frame;}}if (   enc_ctx->codec_tag == AV_RL32("tmcd")&& dec_ctx->time_base.num < dec_ctx->time_base.den&& dec_ctx->time_base.num > 0&& 121LL*dec_ctx->time_base.num > dec_ctx->time_base.den) {enc_ctx->time_base = dec_ctx->time_base;}//如果命令行没设置帧率就用输入流的if (!ost->frame_rate.num)ost->frame_rate = ist->framerate;//帧率取反就是time_baseif(ost->frame_rate.num)enc_ctx->time_base = av_inv_q(ost->frame_rate);//再修正一下帧率av_reduce(&enc_ctx->time_base.num, &enc_ctx->time_base.den,enc_ctx->time_base.num, enc_ctx->time_base.den, INT_MAX);//拷贝side_dataif (ist->st->nb_side_data) {ost->st->side_data = av_realloc_array(NULL, ist->st->nb_side_data,sizeof(*ist->st->side_data));if (!ost->st->side_data)return AVERROR(ENOMEM);ost->st->nb_side_data = 0;for (j = 0; j < ist->st->nb_side_data; j++) {const AVPacketSideData *sd_src = &ist->st->side_data[j];AVPacketSideData *sd_dst = &ost->st->side_data[ost->st->nb_side_data];if (ost->rotate_overridden && sd_src->type == AV_PKT_DATA_DISPLAYMATRIX)continue;sd_dst->data = av_malloc(sd_src->size);if (!sd_dst->data)return AVERROR(ENOMEM);memcpy(sd_dst->data, sd_src->data, sd_src->size);sd_dst->size = sd_src->size;sd_dst->type = sd_src->type;ost->st->nb_side_data++;}}ost->parser = av_parser_init(enc_ctx->codec_id);//针对不同类型的流做拷贝。switch (enc_ctx->codec_type) {case AVMEDIA_TYPE_AUDIO:if (audio_volume != 256) {av_log(NULL, AV_LOG_FATAL, "-acodec copy and -vol are incompatible (frames are not decoded)\n");exit_program(1);}enc_ctx->channel_layout     = dec_ctx->channel_layout;enc_ctx->sample_rate        = dec_ctx->sample_rate;enc_ctx->channels           = dec_ctx->channels;enc_ctx->frame_size         = dec_ctx->frame_size;enc_ctx->audio_service_type = dec_ctx->audio_service_type;enc_ctx->block_align        = dec_ctx->block_align;enc_ctx->initial_padding    = dec_ctx->delay;enc_ctx->profile            = dec_ctx->profile;
#if FF_API_AUDIOENC_DELAYenc_ctx->delay              = dec_ctx->delay;
#endifif((enc_ctx->block_align == 1 || enc_ctx->block_align == 1152 || enc_ctx->block_align == 576) && enc_ctx->codec_id == AV_CODEC_ID_MP3)enc_ctx->block_align= 0;if(enc_ctx->codec_id == AV_CODEC_ID_AC3)enc_ctx->block_align= 0;break;case AVMEDIA_TYPE_VIDEO:enc_ctx->pix_fmt            = dec_ctx->pix_fmt;enc_ctx->colorspace         = dec_ctx->colorspace;enc_ctx->color_range        = dec_ctx->color_range;enc_ctx->color_primaries    = dec_ctx->color_primaries;enc_ctx->color_trc          = dec_ctx->color_trc;enc_ctx->width              = dec_ctx->width;enc_ctx->height             = dec_ctx->height;enc_ctx->has_b_frames       = dec_ctx->has_b_frames;if (ost->frame_aspect_ratio.num) { // overridden by the -aspect cli optionsar =av_mul_q(ost->frame_aspect_ratio,(AVRational){ enc_ctx->height, enc_ctx->width });av_log(NULL, AV_LOG_WARNING, "Overriding aspect ratio ""with stream copy may produce invalid files\n");}else if (ist->st->sample_aspect_ratio.num)sar = ist->st->sample_aspect_ratio;elsesar = dec_ctx->sample_aspect_ratio;ost->st->sample_aspect_ratio = enc_ctx->sample_aspect_ratio = sar;ost->st->avg_frame_rate = ist->st->avg_frame_rate;ost->st->r_frame_rate = ist->st->r_frame_rate;break;case AVMEDIA_TYPE_SUBTITLE:enc_ctx->width  = dec_ctx->width;enc_ctx->height = dec_ctx->height;break;case AVMEDIA_TYPE_UNKNOWN:case AVMEDIA_TYPE_DATA:case AVMEDIA_TYPE_ATTACHMENT:break;default:abort();}} else {//不是流拷贝时,需要重新解码,编码if (!ost->enc)ost->enc = avcodec_find_encoder(enc_ctx->codec_id);//找到编码器if (!ost->enc) {/* should only happen when a default codec is not present. */snprintf(error, sizeof(error), "Encoder (codec %s) not found for output stream #%d:%d",avcodec_get_name(ost->st->codec->codec_id), ost->file_index, ost->index);ret = AVERROR(EINVAL);goto dump_format;}//在ost->st->metadata中设置编码器的信息set_encoder_id(output_files[ost->file_index], ost);#if CONFIG_LIBMFXif (qsv_transcode_init(ost))exit_program(1);
#endif#if CONFIG_CUVIDif (cuvid_transcode_init(ost))exit_program(1);
#endif//在没有设置ost->filter时,命令行没有对滤镜的复杂设置,可见是二选一的。if (!ost->filter &&(enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO ||enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO)) {FilterGraph *fg;fg = init_simple_filtergraph(ist, ost);//后面详细介绍if (configure_filtergraph(fg)) {//后面详细介绍av_log(NULL, AV_LOG_FATAL, "Error opening filters!\n");exit_program(1);}}//如果使视频,就对帧率做处理。if (enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {//首先想办法获得一个帧率if (!ost->frame_rate.num)ost->frame_rate = av_buffersink_get_frame_rate(ost->filter->filter);if (ist && !ost->frame_rate.num)ost->frame_rate = ist->framerate;if (ist && !ost->frame_rate.num)ost->frame_rate = ist->st->r_frame_rate;if (ist && !ost->frame_rate.num) {//默认帧率ost->frame_rate = (AVRational){25, 1};av_log(NULL, AV_LOG_WARNING,"No information ""about the input framerate is available. Falling ""back to a default value of 25fps for output stream #%d:%d. Use the -r option ""if you want a different framerate.\n",ost->file_index, ost->index);}
//                    ost->frame_rate = ist->st->avg_frame_rate.num ? ist->st->avg_frame_rate : (AVRational){25, 1};//根据编码器所支持的帧率找到最接近的if (ost->enc && ost->enc->supported_framerates && !ost->force_fps) {int idx = av_find_nearest_q_idx(ost->frame_rate, ost->enc->supported_framerates);ost->frame_rate = ost->enc->supported_framerates[idx];}// reduce frame rate for mpeg4 to be within the spec limits//如果编码器是mpeg4,帧率有限制,单独设置。if (enc_ctx->codec_id == AV_CODEC_ID_MPEG4) {av_reduce(&ost->frame_rate.num, &ost->frame_rate.den,ost->frame_rate.num, ost->frame_rate.den, 65535);}}//对一些参数的值进行拷贝。设置到enc_ctx中switch (enc_ctx->codec_type) {case AVMEDIA_TYPE_AUDIO:enc_ctx->sample_fmt     = ost->filter->filter->inputs[0]->format;enc_ctx->sample_rate    = ost->filter->filter->inputs[0]->sample_rate;enc_ctx->channel_layout = ost->filter->filter->inputs[0]->channel_layout;enc_ctx->channels       = avfilter_link_get_channels(ost->filter->filter->inputs[0]);enc_ctx->time_base      = (AVRational){ 1, enc_ctx->sample_rate };break;case AVMEDIA_TYPE_VIDEO:enc_ctx->time_base = av_inv_q(ost->frame_rate);if (!(enc_ctx->time_base.num && enc_ctx->time_base.den))enc_ctx->time_base = ost->filter->filter->inputs[0]->time_base;if (   av_q2d(enc_ctx->time_base) < 0.001 && video_sync_method != VSYNC_PASSTHROUGH&& (video_sync_method == VSYNC_CFR || video_sync_method == VSYNC_VSCFR || (video_sync_method == VSYNC_AUTO && !(oc->oformat->flags & AVFMT_VARIABLE_FPS)))){av_log(oc, AV_LOG_WARNING, "Frame rate very high for a muxer not efficiently supporting it.\n""Please consider specifying a lower framerate, a different muxer or -vsync 2\n");}for (j = 0; j < ost->forced_kf_count; j++)ost->forced_kf_pts[j] = av_rescale_q(ost->forced_kf_pts[j],AV_TIME_BASE_Q,enc_ctx->time_base);enc_ctx->width  = ost->filter->filter->inputs[0]->w;enc_ctx->height = ost->filter->filter->inputs[0]->h;enc_ctx->sample_aspect_ratio = ost->st->sample_aspect_ratio =ost->frame_aspect_ratio.num ? // overridden by the -aspect cli optionav_mul_q(ost->frame_aspect_ratio, (AVRational){ enc_ctx->height, enc_ctx->width }) :ost->filter->filter->inputs[0]->sample_aspect_ratio;if (!strncmp(ost->enc->name, "libx264", 7) &&enc_ctx->pix_fmt == AV_PIX_FMT_NONE &&ost->filter->filter->inputs[0]->format != AV_PIX_FMT_YUV420P)av_log(NULL, AV_LOG_WARNING,"No pixel format specified, %s for H.264 encoding chosen.\n""Use -pix_fmt yuv420p for compatibility with outdated media players.\n",av_get_pix_fmt_name(ost->filter->filter->inputs[0]->format));if (!strncmp(ost->enc->name, "mpeg2video", 10) &&enc_ctx->pix_fmt == AV_PIX_FMT_NONE &&ost->filter->filter->inputs[0]->format != AV_PIX_FMT_YUV420P)av_log(NULL, AV_LOG_WARNING,"No pixel format specified, %s for MPEG-2 encoding chosen.\n""Use -pix_fmt yuv420p for compatibility with outdated media players.\n",av_get_pix_fmt_name(ost->filter->filter->inputs[0]->format));enc_ctx->pix_fmt = ost->filter->filter->inputs[0]->format;ost->st->avg_frame_rate = ost->frame_rate;//frame_bits_per_raw_sample,new_video_stream()中有介绍if (!dec_ctx ||enc_ctx->width   != dec_ctx->width  ||enc_ctx->height  != dec_ctx->height ||enc_ctx->pix_fmt != dec_ctx->pix_fmt) {enc_ctx->bits_per_raw_sample = frame_bits_per_raw_sample;}//在指定的时间戳强制关键帧,在ost->forced_keyframes,new_video_stream()中有介绍if (ost->forced_keyframes) {if (!strncmp(ost->forced_keyframes, "expr:", 5)) {ret = av_expr_parse(&ost->forced_keyframes_pexpr, ost->forced_keyframes+5,forced_keyframes_const_names, NULL, NULL, NULL, NULL, 0, NULL);if (ret < 0) {av_log(NULL, AV_LOG_ERROR,"Invalid force_key_frames expression '%s'\n", ost->forced_keyframes+5);return ret;}ost->forced_keyframes_expr_const_values[FKF_N] = 0;ost->forced_keyframes_expr_const_values[FKF_N_FORCED] = 0;ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_N] = NAN;ost->forced_keyframes_expr_const_values[FKF_PREV_FORCED_T] = NAN;// Don't parse the 'forced_keyframes' in case of 'keep-source-keyframes',// parse it only for static kf timings} else if(strncmp(ost->forced_keyframes, "source", 6)) {parse_forced_key_frames(ost->forced_keyframes, ost, ost->enc_ctx);}}break;case AVMEDIA_TYPE_SUBTITLE:enc_ctx->time_base = (AVRational){1, 1000};if (!enc_ctx->width) {enc_ctx->width     = input_streams[ost->source_index]->st->codec->width;enc_ctx->height    = input_streams[ost->source_index]->st->codec->height;}break;case AVMEDIA_TYPE_DATA:break;default:abort();break;}}//设置了disposition参数的情况下做如下操作。存疑if (ost->disposition) {static const AVOption opts[] = {{ "disposition"         , NULL, 0, AV_OPT_TYPE_FLAGS, { .i64 = 0 }, INT64_MIN, INT64_MAX, .unit = "flags" },{ "default"             , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DEFAULT           },    .unit = "flags" },{ "dub"                 , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DUB               },    .unit = "flags" },{ "original"            , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_ORIGINAL          },    .unit = "flags" },{ "comment"             , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_COMMENT           },    .unit = "flags" },{ "lyrics"              , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_LYRICS            },    .unit = "flags" },{ "karaoke"             , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_KARAOKE           },    .unit = "flags" },{ "forced"              , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_FORCED            },    .unit = "flags" },{ "hearing_impaired"    , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_HEARING_IMPAIRED  },    .unit = "flags" },{ "visual_impaired"     , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_VISUAL_IMPAIRED   },    .unit = "flags" },{ "clean_effects"       , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_CLEAN_EFFECTS     },    .unit = "flags" },{ "captions"            , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_CAPTIONS          },    .unit = "flags" },{ "descriptions"        , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DESCRIPTIONS      },    .unit = "flags" },{ "metadata"            , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_METADATA          },    .unit = "flags" },{ NULL },};static const AVClass class = {.class_name = "",.item_name  = av_default_item_name,.option     = opts,.version    = LIBAVUTIL_VERSION_INT,};const AVClass *pclass = &class;ret = av_opt_eval_flags(&pclass, &opts[0], ost->disposition, &ost->st->disposition);if (ret < 0)goto dump_format;}}/* init input streams */for (i = 0; i < nb_input_streams; i++)if ((ret = init_input_stream(i, error, sizeof(error))) < 0) {//后面详细介绍for (i = 0; i < nb_output_streams; i++) {//出错关闭所有输入流对应的编码器ost = output_streams[i];avcodec_close(ost->enc_ctx);}goto dump_format;}/* open each encoder */for (i = 0; i < nb_output_streams; i++) {ret = init_output_stream(output_streams[i], error, sizeof(error));if (ret < 0)goto dump_format;}/* discard unused programs */for (i = 0; i < nb_input_files; i++) {InputFile *ifile = input_files[i];for (j = 0; j < ifile->ctx->nb_programs; j++) {AVProgram *p = ifile->ctx->programs[j];int discard  = AVDISCARD_ALL;for (k = 0; k < p->nb_stream_indexes; k++)if (!input_streams[ifile->ist_index + p->stream_index[k]]->discard) {discard = AVDISCARD_DEFAULT;break;}p->discard = discard;}}/* open files and write file headers */for (i = 0; i < nb_output_files; i++) {oc = output_files[i]->ctx;oc->interrupt_callback = int_cb;if ((ret = avformat_write_header(oc, &output_files[i]->opts)) < 0) {snprintf(error, sizeof(error),"Could not write header for output file #%d ""(incorrect codec parameters ?): %s",i, av_err2str(ret));ret = AVERROR(EINVAL);goto dump_format;}
//         assert_avoptions(output_files[i]->opts);if (strcmp(oc->oformat->name, "rtp")) {want_sdp = 0;}}dump_format:/* dump the file output parameters - cannot be done before in caseof stream copy */for (i = 0; i < nb_output_files; i++) {av_dump_format(output_files[i]->ctx, i, output_files[i]->ctx->filename, 1);}/* dump the stream mapping */av_log(NULL, AV_LOG_INFO, "Stream mapping:\n");for (i = 0; i < nb_input_streams; i++) {ist = input_streams[i];for (j = 0; j < ist->nb_filters; j++) {if (ist->filters[j]->graph->graph_desc) {av_log(NULL, AV_LOG_INFO, "  Stream #%d:%d (%s) -> %s",ist->file_index, ist->st->index, ist->dec ? ist->dec->name : "?",ist->filters[j]->name);if (nb_filtergraphs > 1)av_log(NULL, AV_LOG_INFO, " (graph %d)", ist->filters[j]->graph->index);av_log(NULL, AV_LOG_INFO, "\n");}}}for (i = 0; i < nb_output_streams; i++) {ost = output_streams[i];if (ost->attachment_filename) {/* an attached file */av_log(NULL, AV_LOG_INFO, "  File %s -> Stream #%d:%d\n",ost->attachment_filename, ost->file_index, ost->index);continue;}if (ost->filter && ost->filter->graph->graph_desc) {/* output from a complex graph */av_log(NULL, AV_LOG_INFO, "  %s", ost->filter->name);if (nb_filtergraphs > 1)av_log(NULL, AV_LOG_INFO, " (graph %d)", ost->filter->graph->index);av_log(NULL, AV_LOG_INFO, " -> Stream #%d:%d (%s)\n", ost->file_index,ost->index, ost->enc ? ost->enc->name : "?");continue;}av_log(NULL, AV_LOG_INFO, "  Stream #%d:%d -> #%d:%d",input_streams[ost->source_index]->file_index,input_streams[ost->source_index]->st->index,ost->file_index,ost->index);if (ost->sync_ist != input_streams[ost->source_index])av_log(NULL, AV_LOG_INFO, " [sync #%d:%d]",ost->sync_ist->file_index,ost->sync_ist->st->index);if (ost->stream_copy)av_log(NULL, AV_LOG_INFO, " (copy)");else {const AVCodec *in_codec    = input_streams[ost->source_index]->dec;const AVCodec *out_codec   = ost->enc;const char *decoder_name   = "?";const char *in_codec_name  = "?";const char *encoder_name   = "?";const char *out_codec_name = "?";const AVCodecDescriptor *desc;if (in_codec) {decoder_name  = in_codec->name;desc = avcodec_descriptor_get(in_codec->id);if (desc)in_codec_name = desc->name;if (!strcmp(decoder_name, in_codec_name))decoder_name = "native";}if (out_codec) {encoder_name   = out_codec->name;desc = avcodec_descriptor_get(out_codec->id);if (desc)out_codec_name = desc->name;if (!strcmp(encoder_name, out_codec_name))encoder_name = "native";}av_log(NULL, AV_LOG_INFO, " (%s (%s) -> %s (%s))",in_codec_name, decoder_name,out_codec_name, encoder_name);}av_log(NULL, AV_LOG_INFO, "\n");}if (ret) {av_log(NULL, AV_LOG_ERROR, "%s\n", error);return ret;}if (sdp_filename || want_sdp) {print_sdp();}transcode_init_done = 1;return 0;
}

简单滤镜涉及到的结构体

typedef struct InputFilter {AVFilterContext    *filter;struct InputStream *ist;struct FilterGraph *graph;uint8_t            *name;
} InputFilter;typedef struct OutputFilter {AVFilterContext     *filter;struct OutputStream *ost;struct FilterGraph  *graph;uint8_t             *name;/* temporary storage until stream maps are processed */AVFilterInOut       *out_tmp;enum AVMediaType     type;
} OutputFilter;typedef struct FilterGraph {int            index;const char    *graph_desc;AVFilterGraph *graph;int reconfiguration;InputFilter   **inputs;int          nb_inputs;OutputFilter **outputs;int         nb_outputs;
} FilterGraph;


init_simple_filtergraph()


绿线表示赋值的指向。

FilterGraph *init_simple_filtergraph(InputStream *ist, OutputStream *ost)
{FilterGraph *fg = av_mallocz(sizeof(*fg));if (!fg)exit_program(1);//针对一路流有一个fg,此句表示这个fg在全局链表filtergraphs中的下标fg->index = nb_filtergraphs;//对输出流处理赋值GROW_ARRAY(fg->outputs, fg->nb_outputs);if (!(fg->outputs[0] = av_mallocz(sizeof(*fg->outputs[0]))))exit_program(1);fg->outputs[0]->ost   = ost;fg->outputs[0]->graph = fg;ost->filter = fg->outputs[0];GROW_ARRAY(fg->inputs, fg->nb_inputs);if (!(fg->inputs[0] = av_mallocz(sizeof(*fg->inputs[0]))))exit_program(1);fg->inputs[0]->ist   = ist;fg->inputs[0]->graph = fg;//是否因为多个输出流可以针对一个输入流所以输入流中有多个滤镜信息。这个需要再看,暂且这么理解GROW_ARRAY(ist->filters, ist->nb_filters);ist->filters[ist->nb_filters - 1] = fg->inputs[0];//把这个fg放到filtergraphs中GROW_ARRAY(filtergraphs, nb_filtergraphs);filtergraphs[nb_filtergraphs - 1] = fg;return fg;
}

configure_filtergraph()

int configure_filtergraph(FilterGraph *fg)
{AVFilterInOut *inputs, *outputs, *cur;int ret, i, simple = !fg->graph_desc;//NULL时simple为1;否则为0;/*ost->avfilter在new_video_stream()中设置,视频为"null" ,音频为 "anull",或者命令行设置*/const char *graph_desc = simple ? fg->outputs[0]->ost->avfilter :fg->graph_desc;//初始化fg->graphavfilter_graph_free(&fg->graph);if (!(fg->graph = avfilter_graph_alloc()))return AVERROR(ENOMEM);/*把命令行中设置到ost->sws_dict、ost->swr_opts、ost->resample_opts中的配置信息,配置到fg->graph中*/if (simple) {OutputStream *ost = fg->outputs[0]->ost;//在init_simple_filtergraph()中赋值了。char args[512];AVDictionaryEntry *e = NULL;args[0] = 0;/*ost->sws_dict->elems{key = "flags", value = "bicubic"}。这里至少有这个值。它是图像放大的一种插值方法(双三次插值),应该是在放大分辨率的时候用*/while ((e = av_dict_get(ost->sws_dict, "", e,AV_DICT_IGNORE_SUFFIX))) {av_strlcatf(args, sizeof(args), "%s=%s:", e->key, e->value);}if (strlen(args))args[strlen(args)-1] = 0;fg->graph->scale_sws_opts = av_strdup(args);args[0] = 0;while ((e = av_dict_get(ost->swr_opts, "", e,AV_DICT_IGNORE_SUFFIX))) {av_strlcatf(args, sizeof(args), "%s=%s:", e->key, e->value);}if (strlen(args))args[strlen(args)-1] = 0;av_opt_set(fg->graph, "aresample_swr_opts", args, 0);args[0] = '\0';while ((e = av_dict_get(fg->outputs[0]->ost->resample_opts, "", e,AV_DICT_IGNORE_SUFFIX))) {av_strlcatf(args, sizeof(args), "%s=%s:", e->key, e->value);}if (strlen(args))args[strlen(args) - 1] = '\0';fg->graph->resample_lavr_opts = av_strdup(args);e = av_dict_get(ost->encoder_opts, "threads", NULL, 0);if (e)av_opt_set(fg->graph, "threads", e->value, 0);}if ((ret = avfilter_graph_parse2(fg->graph, graph_desc, &inputs, &outputs)) < 0)return ret;if (hw_device_ctx) {for (i = 0; i < fg->graph->nb_filters; i++) {fg->graph->filters[i]->hw_device_ctx = av_buffer_ref(hw_device_ctx);}}//对avfilter_graph_parse2()返回的inputs和outputs进行判断,并输出错误原因。if (simple && (!inputs || inputs->next || !outputs || outputs->next)) {const char *num_inputs;const char *num_outputs;if (!outputs) {num_outputs = "0";} else if (outputs->next) {num_outputs = ">1";} else {num_outputs = "1";}if (!inputs) {num_inputs = "0";} else if (inputs->next) {num_inputs = ">1";} else {num_inputs = "1";}av_log(NULL, AV_LOG_ERROR, "Simple filtergraph '%s' was expected ""to have exactly 1 input and 1 output."" However, it had %s input(s) and %s output(s)."" Please adjust, or use a complex filtergraph (-filter_complex) instead.\n",graph_desc, num_inputs, num_outputs);return AVERROR(EINVAL);}//在simple为0时,inputs->next和outputs->next可能有值。//配置输入滤镜    for (cur = inputs, i = 0; cur; cur = cur->next, i++)if ((ret = configure_input_filter(fg, fg->inputs[i], cur)) < 0) {//在后面有详细讨论avfilter_inout_free(&inputs);avfilter_inout_free(&outputs);return ret;}avfilter_inout_free(&inputs);//配置输出滤镜for (cur = outputs, i = 0; cur; cur = cur->next, i++)configure_output_filter(fg, fg->outputs[i], cur);//在后面有详细讨论avfilter_inout_free(&outputs);if ((ret = avfilter_graph_config(fg->graph, NULL)) < 0)return ret;//只在configure_input_audio_filter中用过。用处存疑fg->reconfiguration = 1;for (i = 0; i < fg->nb_outputs; i++) {OutputStream *ost = fg->outputs[i]->ost;if (!ost->enc) {/* identical to the same check in ffmpeg.c, needed becausecomplex filter graphs are initialized earlier */av_log(NULL, AV_LOG_ERROR, "Encoder (codec %s) not found for output stream #%d:%d\n",avcodec_get_name(ost->st->codec->codec_id), ost->file_index, ost->index);return AVERROR(EINVAL);}if (ost->enc->type == AVMEDIA_TYPE_AUDIO &&!(ost->enc->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE))av_buffersink_set_frame_size(ost->filter->filter,ost->enc_ctx->frame_size);}return 0;
}

configure_input_filter()

static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter,AVFilterInOut *in)
{av_freep(&ifilter->name);DESCRIBE_FILTER_LINK(ifilter, in, 1);//后面介绍if (!ifilter->ist->dec) {av_log(NULL, AV_LOG_ERROR,"No decoder for stream #%d:%d, filtering impossible\n",ifilter->ist->file_index, ifilter->ist->st->index);return AVERROR_DECODER_NOT_FOUND;}switch (avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx)) {case AVMEDIA_TYPE_VIDEO: return configure_input_video_filter(fg, ifilter, in);case AVMEDIA_TYPE_AUDIO: return configure_input_audio_filter(fg, ifilter, in);default: av_assert0(0);}
}

DESCRIBE_FILTER_LINK()

这个需要分析完滤镜的原理后回过头来再解释。存疑

#define DESCRIBE_FILTER_LINK(f, inout, in)                         \
{                                                                  \AVFilterContext *ctx = inout->filter_ctx;                      \AVFilterPad *pads = in ? ctx->input_pads  : ctx->output_pads;  \int       nb_pads = in ? ctx->nb_inputs   : ctx->nb_outputs;   \AVIOContext *pb;                                               \\if (avio_open_dyn_buf(&pb) < 0)                                \exit_program(1);                                           \\avio_printf(pb, "%s", ctx->filter->name);                      \if (nb_pads > 1)                                               \avio_printf(pb, ":%s", avfilter_pad_get_name(pads, inout->pad_idx));\avio_w8(pb, 0);                                                \avio_close_dyn_buf(pb, &f->name);                              \
}
//例子
DESCRIBE_FILTER_LINK(ifilter, in, 1);
{AVFilterContext *ctx = in->filter_ctx;                      \AVFilterPad *pads = 1 ? ctx->input_pads  : ctx->output_pads;  \int       nb_pads = 1 ? ctx->nb_inputs   : ctx->nb_outputs;   \AVIOContext *pb;                                               \\if (avio_open_dyn_buf(&pb) < 0)                                \exit_program(1);                                           \\avio_printf(pb, "%s", ctx->filter->name);                      \if (nb_pads > 1)                                               \avio_printf(pb, ":%s", avfilter_pad_get_name(pads, in->pad_idx));\avio_w8(pb, 0);                                                \avio_close_dyn_buf(pb, &ifilter->name);
}

configure_input_video_filter()

配置输入视频滤镜。

static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,AVFilterInOut *in)
{AVFilterContext *last_filter;//buffer型滤镜定义在libavfilter\buffersrc.c中const AVFilter *buffer_filt = avfilter_get_by_name("buffer");//取输入流的基本信息InputStream *ist = ifilter->ist;InputFile     *f = input_files[ist->file_index];AVRational tb = ist->framerate.num ? av_inv_q(ist->framerate) :ist->st->time_base;AVRational fr = ist->framerate;AVRational sar;AVBPrint args;char name[255];int ret, pad_idx = 0;int64_t tsoffset = 0;//此结构体描述将要通过此滤镜的帧信息AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();//对par初始化if (!par)return AVERROR(ENOMEM);memset(par, 0, sizeof(*par));par->format = AV_PIX_FMT_NONE;if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {av_log(NULL, AV_LOG_ERROR, "Cannot connect video filter to audio input\n");ret = AVERROR(EINVAL);goto fail;}//如果输入流的帧率之前不知道的话,从这里获取,也许不准if (!fr.num)fr = av_guess_frame_rate(input_files[ist->file_index]->ctx, ist->st, NULL);if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {//为字幕流转视频做准备ret = sub2video_prepare(ist);if (ret < 0)goto fail;}//采样的纵横比sar = ist->st->sample_aspect_ratio.num ?ist->st->sample_aspect_ratio :ist->dec_ctx->sample_aspect_ratio;if(!sar.den)sar = (AVRational){0,1};//args是一个自增型的buf,不用担心越界。av_bprint_init(&args, 0, 1);//组avfilter_graph_create_filter函数需要用到的字符串。av_bprintf(&args,"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:""pixel_aspect=%d/%d:sws_param=flags=%d", ist->resample_width,ist->resample_height,ist->hwaccel_retrieve_data ? ist->hwaccel_retrieved_pix_fmt : ist->resample_pix_fmt,tb.num, tb.den, sar.num, sar.den,SWS_BILINEAR + ((ist->dec_ctx->flags&AV_CODEC_FLAG_BITEXACT) ? SWS_BITEXACT:0));if (fr.num && fr.den)av_bprintf(&args, ":frame_rate=%d/%d", fr.num, fr.den);snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index,ist->file_index, ist->st->index);if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name,args.str, NULL, fg->graph)) < 0)goto fail;par->hw_frames_ctx = ist->hw_frames_ctx;ret = av_buffersrc_parameters_set(ifilter->filter, par);if (ret < 0)goto fail;av_freep(&par);last_filter = ifilter->filter;/*在add_input_streams()中设置。默认是1.init_options中设置的此值是0;"automatically insert correct rotate filters"自动插入正确的旋转过滤器*/if (ist->autorotate) {double theta = get_rotation(ist->st);if (fabs(theta - 90) < 1.0) {ret = insert_filter(&last_filter, &pad_idx, "transpose", "clock");} else if (fabs(theta - 180) < 1.0) {ret = insert_filter(&last_filter, &pad_idx, "hflip", NULL);if (ret < 0)return ret;ret = insert_filter(&last_filter, &pad_idx, "vflip", NULL);} else if (fabs(theta - 270) < 1.0) {ret = insert_filter(&last_filter, &pad_idx, "transpose", "cclock");} else if (fabs(theta) > 1.0) {char rotate_buf[64];snprintf(rotate_buf, sizeof(rotate_buf), "%f*PI/180", theta);ret = insert_filter(&last_filter, &pad_idx, "rotate", rotate_buf);}if (ret < 0)return ret;}//设置帧率if (ist->framerate.num) {AVFilterContext *setpts;snprintf(name, sizeof(name), "force CFR for input from stream %d:%d",ist->file_index, ist->st->index);if ((ret = avfilter_graph_create_filter(&setpts,avfilter_get_by_name("setpts"),name, "N", NULL,fg->graph)) < 0)return ret;if ((ret = avfilter_link(last_filter, 0, setpts, 0)) < 0)return ret;last_filter = setpts;}//设置反交错滤镜,处理视频由于缩放导致的拉丝现象if (do_deinterlace) {AVFilterContext *yadif;snprintf(name, sizeof(name), "deinterlace input from stream %d:%d",ist->file_index, ist->st->index);if ((ret = avfilter_graph_create_filter(&yadif,avfilter_get_by_name("yadif"),name, "", NULL,fg->graph)) < 0)return ret;if ((ret = avfilter_link(last_filter, 0, yadif, 0)) < 0)return ret;last_filter = yadif;}snprintf(name, sizeof(name), "trim for input stream %d:%d",ist->file_index, ist->st->index);//copy_ts对应参数"copyts","copy timestamps";//start_at_zero对应参数"start_at_zero"         if (copy_ts) {tsoffset = f->start_time == AV_NOPTS_VALUE ? 0 : f->start_time;if (!start_at_zero && f->ctx->start_time != AV_NOPTS_VALUE)tsoffset += f->ctx->start_time;}//根据传入的第一开始时间,第二参数时长,设置裁剪视频滤镜ret = insert_trim(((f->start_time == AV_NOPTS_VALUE) || !f->accurate_seek) ?AV_NOPTS_VALUE : tsoffset, f->recording_time,&last_filter, &pad_idx, name);if (ret < 0)return ret;if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0)return ret;return 0;
fail:av_freep(&par);return ret;
}

configure_output_filter

配置输出滤镜

int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
{av_freep(&ofilter->name);DESCRIBE_FILTER_LINK(ofilter, out, 0);if (!ofilter->ost) {av_log(NULL, AV_LOG_FATAL, "Filter %s has an unconnected output\n", ofilter->name);exit_program(1);}switch (avfilter_pad_get_type(out->filter_ctx->output_pads, out->pad_idx)) {case AVMEDIA_TYPE_VIDEO: return configure_output_video_filter(fg, ofilter, out);case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, ofilter, out);default: av_assert0(0);}
}

configure_output_video_filter

static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
{char *pix_fmts;OutputStream *ost = ofilter->ost;OutputFile    *of = output_files[ost->file_index];AVCodecContext *codec = ost->enc_ctx;AVFilterContext *last_filter = out->filter_ctx;int pad_idx = out->pad_idx;int ret;char name[255];snprintf(name, sizeof(name), "output stream %d:%d", ost->file_index, ost->index);ret = avfilter_graph_create_filter(&ofilter->filter,avfilter_get_by_name("buffersink"),name, NULL, NULL, fg->graph);if (ret < 0)return ret;if (!hw_device_ctx && (codec->width || codec->height)) {char args[255];AVFilterContext *filter;AVDictionaryEntry *e = NULL;//设置有关libswscale库参数的滤镜,修改视频的长宽就是用此滤镜snprintf(args, sizeof(args), "%d:%d",codec->width,codec->height);//在cmdutils.c的opt_default()函数中判断为libswscale库相关参数被存放在ost->sws_dict中,逐个取出,待设置while ((e = av_dict_get(ost->sws_dict, "", e,AV_DICT_IGNORE_SUFFIX))) {av_strlcatf(args, sizeof(args), ":%s=%s", e->key, e->value);}snprintf(name, sizeof(name), "scaler for output stream %d:%d",ost->file_index, ost->index);if ((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"),name, args, NULL, fg->graph)) < 0)return ret;if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0)return ret;last_filter = filter;pad_idx = 0;}//设置像素格式4:2:0等if ((pix_fmts = choose_pix_fmts(ost))) {AVFilterContext *filter;snprintf(name, sizeof(name), "pixel format for output stream %d:%d",ost->file_index, ost->index);ret = avfilter_graph_create_filter(&filter,avfilter_get_by_name("format"),"format", pix_fmts, NULL, fg->graph);av_freep(&pix_fmts);if (ret < 0)return ret;if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0)return ret;last_filter = filter;pad_idx     = 0;}//设置输出帧率if (ost->frame_rate.num && 0) {AVFilterContext *fps;char args[255];snprintf(args, sizeof(args), "fps=%d/%d", ost->frame_rate.num,ost->frame_rate.den);snprintf(name, sizeof(name), "fps for output stream %d:%d",ost->file_index, ost->index);ret = avfilter_graph_create_filter(&fps, avfilter_get_by_name("fps"),name, args, NULL, fg->graph);if (ret < 0)return ret;ret = avfilter_link(last_filter, pad_idx, fps, 0);if (ret < 0)return ret;last_filter = fps;pad_idx = 0;}//设置裁剪的时间。snprintf(name, sizeof(name), "trim for output stream %d:%d",ost->file_index, ost->index);ret = insert_trim(of->start_time, of->recording_time,&last_filter, &pad_idx, name);if (ret < 0)return ret;if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0)return ret;return 0;
}

init_input_stream

赋值读帧的回调函数,打开对应的解码器

static int init_input_stream(int ist_index, char *error, int error_len)
{int ret;InputStream *ist = input_streams[ist_index];if (ist->decoding_needed) {AVCodec *codec = ist->dec;if (!codec) {snprintf(error, error_len, "Decoder (codec %s) not found for input stream #%d:%d",avcodec_get_name(ist->dec_ctx->codec_id), ist->file_index, ist->st->index);return AVERROR(EINVAL);}ist->dec_ctx->opaque                = ist;//用于协商一个像素格式。ist->dec_ctx->get_format            = get_format;//读取数据的回调函数,默认调用avcodec_default_get_buffer2()ist->dec_ctx->get_buffer2           = get_buffer;ist->dec_ctx->thread_safe_callbacks = 1;av_opt_set_int(ist->dec_ctx, "refcounted_frames", 1, 0);if (ist->dec_ctx->codec_id == AV_CODEC_ID_DVB_SUBTITLE &&(ist->decoding_needed & DECODING_FOR_OST)) {av_dict_set(&ist->decoder_opts, "compute_edt", "1", AV_DICT_DONT_OVERWRITE);if (ist->decoding_needed & DECODING_FOR_FILTER)av_log(NULL, AV_LOG_WARNING, "Warning using DVB subtitles for filtering and output at the same time is not fully supported, also see -compute_edt [0|1]\n");}av_dict_set(&ist->decoder_opts, "sub_text_format", "ass", AV_DICT_DONT_OVERWRITE);if (!av_dict_get(ist->decoder_opts, "threads", NULL, 0))av_dict_set(&ist->decoder_opts, "threads", "auto", 0);//打开解码器if ((ret = avcodec_open2(ist->dec_ctx, codec, &ist->decoder_opts)) < 0) {if (ret == AVERROR_EXPERIMENTAL)abort_codec_experimental(codec, 0);snprintf(error, error_len,"Error while opening decoder for input stream ""#%d:%d : %s",ist->file_index, ist->st->index, av_err2str(ret));return ret;}assert_avoptions(ist->decoder_opts);}ist->next_pts = AV_NOPTS_VALUE;ist->next_dts = AV_NOPTS_VALUE;return 0;
}

transcode_init()函数介绍相关推荐

  1. python3 转码的函数_python基础3之文件操作、字符编码解码、函数介绍

    内容概要: 一.文件操作 二.字符编码解码 三.函数介绍 一.文件操作 文件操作流程: 打开文件,得到文件句柄并赋值给一个变量 通过句柄对文件进行操作 关闭文件 基本操作: 1 #/usr/bin/e ...

  2. C语言中的scanf()函数介绍

    1.scanf函数:读取从键盘输入的数据 在C语言中,有多个函数可以从键盘获得用户输入: scanf():和 printf() 类似,scanf() 可以输入多种类型的数据 getchar().get ...

  3. 延时函数介绍和呼吸灯的实现

    文章目录 延时函数介绍 呼吸灯原理 杨桃32学习笔记,本文图片文字皆为转述 延时函数介绍 分为delay_s秒,delay_ms毫秒,delay_us微秒延时,最大参数不能超过65535. 呼吸灯原理 ...

  4. ×××S 2012 参照函数 -- 介绍

    ×××S 2012 参照函数 -- 介绍 在×××S中,一张报表内可以同时包含多个数据集,但是一个数据区域就仅限于一个数据集,如果希望同时参考多个数据集,查找函数就能轻松办到,其概念类似JOIN是通过 ...

  5. ffmpeg源码分析:transcode_init()函数

    2019独角兽企业重金招聘Python工程师标准>>> transcode_init()函数是在转换前做准备工作的.此处看一下它的真面目,不废话,看注释吧: //为转换过程做准备 s ...

  6. 【 MATLAB 】rem 函数介绍

    rem函数和mod函数很相似,二者认真看一个,另一个看一下区别即可. mod函数介绍:[ MATLAB ]mod 函数介绍 rem Remainder after division Syntax r ...

  7. 【 MATLAB 】filter 函数介绍 之 Filter Data in Sections

    [ MATLAB ]filter 函数介绍(一维数字滤波器) 在上篇博文中,里面有一个例子,就是过滤部分中的数据,这个部分中的数据的意思是如果有一个向量需要过滤,我们可以把它分为几段,然后分段过滤. ...

  8. swift1.2语言函数和闭包函数介绍

    swift1.2语言函数和闭包函数介绍 在编程中,随着处理问题的越来越复杂,代码量飞速增加.其中,大量的代码往往相互重复或者近似重复.如果不采有效方式加以解决,代码将很难维护. swift1.2语言函 ...

  9. php函数介绍,PHP函数介绍_PHP教程

    PHP函数介绍 基本使用: 函数定义形式: function 函数名(形参1,形参2,...) { //函数体(代码块): } 函数调用形式: 本质上就是使用一个名字来达到执行其中函数中的的作用.通常 ...

最新文章

  1. python数据分析需要数据库吗_python数据分析|使用python操作MySQL数据库
  2. Thinkphp3.23 关联模型relation方法不存在解决方法
  3. Linux开机运行应用程序
  4. mysql.host_mysql启动提示mysql.host 不存在,启动失败的解决方法
  5. New(new 运算符)
  6. 802.1x客户端 linux,Gentoo Linux--校园网(802.1x)拨号认证客户端解决方案
  7. python遗传算法求解TSP问题
  8. 分析CHE矢量变频器在数控雕刻机床上应用
  9. PNP三极管限流电路分析
  10. ADS仿真功率放大器模型导入报错问题解决
  11. 昼夜系统-游戏中的时间
  12. 【观察】戴尔科技:未来企业的创新平台,数字中国的坚实底座
  13. matlab计算叶子的面积,基于MATLAB进行树叶面积测量实验报告
  14. 【论文分享】BERTifying the Hidden Markov Model for Multi-Source Weakly Supervised Named Entity Recognition
  15. 机器学习之线性回归_通过线性回归开始机器学习之旅
  16. python伪原创工具开发_现在有哪些好用的伪原创工具?
  17. c语言 json 请求_JSON的简单介绍以及C语言的JSON库使用
  18. 30天敏捷结果(3):用三个故事驱动你的一天
  19. 小白都能学会的Python基础 第二讲:Python基础知识
  20. Python项目实战:使用PySpark对大数据进行分析

热门文章

  1. 关于 英文的 金额转换
  2. 【渝粤教育】国家开放大学2018年春季 7067-21T (1)康复护理学 参考试题
  3. 一个项目配置多数据源Aop调用
  4. windows与linux中,ping大数据包的命令格式
  5. 子桓说:学几招商业套路,以后用的上!
  6. Git从安装到使用:大白话学习历程(一)
  7. 初中使用计算机教学反思,初中信息技术教学反思(通用5篇)
  8. 红杉中国战略投资诺亚财富
  9. 查看oracle版本及补丁,检查及升级Oracle数据库补丁版本
  10. 邮箱扒头像来告诉你怎么写简单的脚本扒图