FFmpeg源码剖析-通用:ffmpeg_parse_options()

本文详细介绍了FFmpeg中ffmpeg_parse_options()函数的作用,该函数负责解析命令行参数、打开输入和输出文件。在解析过程中,它涉及到split_commandline()、parse_optgroup()和open_files()等函数,用于处理输入输出文件的打开和解码编码工作。在打开输入文件时,通过avformat_find_stream_info()分析流信息并匹配解码器;在打开输出文件时,设置编码器参数并写入文件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ffmpeg_parse_options()函数位于ffmpeg_opt.c
 

1. 函数概述

它的功能主要有三个,
解析命令行参数;
打开输入文件,并解析数据,找到匹配每一个视频,音频,数据流的解码器;
打开输出文件,并设置好输出的视频,音频,数据流的编码器;

 

2. 函数调用结构图


图 ffmpeg_parse_options()函数调用结构


3. 代码分析
int ffmpeg_parse_options(int argc, char **argv)
{
    OptionParseContext octx;


    /* split the commandline into an internal representation */


    ret = split_commandline(&octx, argc, argv, options, groups,


                            FF_ARRAY_ELEMS(groups));


    /* apply global options */


    ret = parse_optgroup(NULL, &octx.global_opts);


    /* open input files */


    ret = open_files(&octx.groups[GROUP_INFILE], "input", open_input_file);


    /* open output files */


    ret = open_files(&octx.groups[GROUP_OUTFILE], "output", open_output_file);


    return ret;


}
它主要调用了两次open_files()函数,一次用于打开输入文件,一次用于打开输出文件。


3.1 open_files()
static int open_files(OptionGroupList *l, const char *inout,


                      int (*open_file)(OptionsContext*, const char*))
{


    int i, ret;




    for (i = 0; i < l->nb_groups; i++) {


        OptionGroup *g = &l->groups[i];


        OptionsContext o;




        init_options(&o);


        o.g = g;




        ret = parse_optgroup(&o, g);


        if (ret < 0) {


            av_log(NULL, AV_LOG_ERROR, "Error parsing options for %s file "


                   "%s.\n", inout, g->arg);


            return ret;


        }




        av_log(NULL, AV_LOG_DEBUG, "Opening an %s file: %s.\n", inout, g->arg);


        ret = open_file(&o, g->arg);


        uninit_options(&o);


        if (ret < 0) {


            av_log(NULL, AV_LOG_ERROR, "Error opening %s file %s.\n",


                   inout, g->arg);


            return ret;


        }


        av_log(NULL, AV_LOG_DEBUG, "Successfully opened the file.\n");


    }




    return 0;


}


用for循环,是当有多个输入文件时,需要一一打开;
其中open_file()是主要函数,它是一个函数指针,运行时调用的是指针所指向的函数。


3.2 open_input_file()
static int open_input_file(OptionsContext *o, const char *filename)
{
    InputFile *f;
    AVFormatContext *ic;
    AVInputFormat *file_iformat = NULL;
    int err, i, ret;
    int64_t timestamp;
    AVDictionary **opts;
    AVDictionary *unused_opts = NULL;
    AVDictionaryEntry *e = NULL;
    int orig_nb_streams;                     // number of streams before avformat_find_stream_info
    char *   video_codec_name = NULL;
    char *   audio_codec_name = NULL;
    char *subtitle_codec_name = NULL;
    char *    data_codec_name = NULL;
    int scan_all_pmts_set = 0;


    if (o->format) {
        if (!(file_iformat = av_find_input_format(o->format))) {
            av_log(NULL, AV_LOG_FATAL, "Unknown input format: '%s'\n", o->format);
            exit_program(1);
        }
    }


    if (!strcmp(filename, "-"))
        filename = "pipe:";


    stdin_interaction &= strncmp(filename, "pipe:", 5) &&
                         strcmp(filename, "/dev/stdin");


    /* get default parameters from command line */
    ic = avformat_alloc_context();
    if (!ic) {
        print_error(filename, AVERROR(ENOMEM));
        exit_program(1);
    }


/* 这些参数没有设置的话,这段代码都不会执行*/
...
/* END */


    /* If not enough info to get the stream parameters, we decode the
       first frames to get it. (used in mpeg case for example) */
    /* 读取文件数据,并进行相应的流分析,找到匹配的解码器 */
    ret = avformat_find_stream_info(ic, opts);
    if (ret < 0) {
        av_log(NULL, AV_LOG_FATAL, "%s: could not find codec parameters\n", filename);
        if (ic->nb_streams == 0) {
            avformat_close_input(&ic);
            exit_program(1);
        }
    }


    if (o->start_time_eof != AV_NOPTS_VALUE) {
        if (ic->duration>0) {
            o->start_time = o->start_time_eof + ic->duration;
        } else
            av_log(NULL, AV_LOG_WARNING, "Cannot use -sseof, duration of %s not known\n", filename);
    }
    timestamp = (o->start_time == AV_NOPTS_VALUE) ? 0 : o->start_time;
    /* add the stream start time */
    if (!o->seek_timestamp && ic->start_time != AV_NOPTS_VALUE)
        timestamp += ic->start_time;


    /* if seeking requested, we execute it */
    if (o->start_time != AV_NOPTS_VALUE) {
        int64_t seek_timestamp = timestamp;


        if (!(ic->iformat->flags & AVFMT_SEEK_TO_PTS)) {
            int dts_heuristic = 0;
            for (i=0; i<ic->nb_streams; i++) {
                const AVCodecParameters *par = ic->streams[i]->codecpar;
                if (par->video_delay)
                    dts_heuristic = 1;
            }
            if (dts_heuristic) {
                seek_timestamp -= 3*AV_TIME_BASE / 23;
            }
        }
        ret = avformat_seek_file(ic, -1, INT64_MIN, seek_timestamp, seek_timestamp, 0);
        if (ret < 0) {
            av_log(NULL, AV_LOG_WARNING, "%s: could not seek to position %0.3f\n",
                   filename, (double)timestamp / AV_TIME_BASE);
        }
    }


    /* update the current parameters so that they match the one of the input stream */
    add_input_streams(o, ic);


    /* 在这个地方会打印出所有解析出来的输入文件的信息,如:
    Input #0, flv, from '/opt/ffmpeg/test-samples/cosmos-30fps.flv':
    Metadata:
    major_brand     : isom
    minor_version   : 1
    compatible_brands: isomavc1
    encoder         : Lavf56.4.101
    server          : 
    srs_primary     : 
    srs_authors     : 
    server_version  : 2.0.209
    Duration: 00:02:06.38, start: 0.010000, bitrate: 615 kb/s
    Stream #0:0: Audio: aac (LC), 48000 Hz, stereo, fltp
    Stream #0:1: Video: h264 (High), yuv420p(progressive), 640x480 [SAR 135:101 DAR 180:101], 30.30 fps, 30 tbr, 1k tbn, 60 tbc
1067        GROW_ARRAY(input_files, nb_input_files);
     */
    av_dump_format(ic, nb_input_files, filename, 0);


    GROW_ARRAY(input_files, nb_input_files);
    f = av_mallocz(sizeof(*f));
    if (!f)
        exit_program(1);
    input_files[nb_input_files - 1] = f;


    f->ctx        = ic;
    f->ist_index  = nb_input_streams - ic->nb_streams;
    f->start_time = o->start_time;
    f->recording_time = o->recording_time;
    f->input_ts_offset = o->input_ts_offset;
    f->ts_offset  = o->input_ts_offset - (copy_ts ? (start_at_zero && ic->start_time != AV_NOPTS_VALUE ? ic->start_time : 0) : timestamp);
    f->nb_streams = ic->nb_streams;
    f->rate_emu   = o->rate_emu;
    f->accurate_seek = o->accurate_seek;
    f->loop = o->loop;
    f->duration = 0;
    f->time_base = (AVRational){ 1, 1 };
#if HAVE_PTHREADS
    f->thread_queue_size = o->thread_queue_size > 0 ? o->thread_queue_size : 8;
#endif


    /* check if all codec options have been used */
    unused_opts = strip_specifiers(o->g->codec_opts);
    for (i = f->ist_index; i < nb_input_streams; i++) {
        e = NULL;
        while ((e = av_dict_get(input_streams[i]->decoder_opts, "", e,
                                AV_DICT_IGNORE_SUFFIX)))
            av_dict_set(&unused_opts, e->key, NULL, 0);
    }


    e = NULL;
    while ((e = av_dict_get(unused_opts, "", e, AV_DICT_IGNORE_SUFFIX))) {
        const AVClass *class = avcodec_get_class();
        const AVOption *option = av_opt_find(&class, e->key, NULL, 0,
                                             AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
        const AVClass *fclass = avformat_get_class();
        const AVOption *foption = av_opt_find(&fclass, e->key, NULL, 0,
                                             AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
        if (!option || foption)
            continue;




        if (!(option->flags & AV_OPT_FLAG_DECODING_PARAM)) {
            av_log(NULL, AV_LOG_ERROR, "Codec AVOption %s (%s) specified for "
                   "input file #%d (%s) is not a decoding option.\n", e->key,
                   option->help ? option->help : "", nb_input_files - 1,
                   filename);
            exit_program(1);
        }


        av_log(NULL, AV_LOG_WARNING, "Codec AVOption %s (%s) specified for "
               "input file #%d (%s) has not been used for any stream. The most "
               "likely reason is either wrong type (e.g. a video option with "
               "no video streams) or that it is a private option of some decoder "
               "which was not actually used for any stream.\n", e->key,
               option->help ? option->help : "", nb_input_files - 1, filename);
    }
    av_dict_free(&unused_opts);


    for (i = 0; i < o->nb_dump_attachment; i++) {
        int j;


        for (j = 0; j < ic->nb_streams; j++) {
            AVStream *st = ic->streams[j];


            if (check_stream_specifier(ic, st, o->dump_attachment[i].specifier) == 1)
                dump_attachment(st, o->dump_attachment[i].u.str);
        }
    }


    for (i = 0; i < orig_nb_streams; i++)
        av_dict_free(&opts[i]);
    av_freep(&opts);


    input_stream_potentially_available = 1;


    return 0;
}


3.2.1 avformat_find_stream_info()
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
{
    int i, count = 0, ret = 0, j;
    int64_t read_size;
    AVStream *st;
    AVCodecContext *avctx;
    AVPacket pkt1, *pkt;
    int64_t old_offset  = avio_tell(ic->pb);
    // new streams might appear, no options for those
    int orig_nb_streams = ic->nb_streams;
    int flush_codecs;
    int64_t max_analyze_duration = ic->max_analyze_duration;
    int64_t max_stream_analyze_duration;
    int64_t max_subtitle_analyze_duration;
    int64_t probesize = ic->probesize;
    int eof_reached = 0;
    int *missing_streams = av_opt_ptr(ic->iformat->priv_class, ic->priv_data, "missing_streams");


    flush_codecs = probesize > 0;


    av_opt_set(ic, "skip_clear", "1", AV_OPT_SEARCH_CHILDREN);


    max_stream_analyze_duration = max_analyze_duration;
    max_subtitle_analyze_duration = max_analyze_duration;


    /* 如果没有在命令行参数中设置 max_analyze_duration, 
       则设置max_stream_analyze_duration ,max_analyze_duration 为系统默认最大的分析时长(默认值为5000000)
       设置 max_subtitle_analyze_duration 为30000000.
       AV_TIME_BASE是FFmpeg内部的时钟基准的整数表示, 
       #define AV_TIME_BASE 1000000
     */
    if (!max_analyze_duration) {
        max_stream_analyze_duration =
        max_analyze_duration        = 5*AV_TIME_BASE;
        max_subtitle_analyze_duration = 30*AV_TIME_BASE;


        /* 如果输入文件是FLV格式,则需要设置更长的分析时间
        if (!strcmp(ic->iformat->name, "flv"))
            max_stream_analyze_duration = 90*AV_TIME_BASE;
        if (!strcmp(ic->iformat->name, "mpeg") || !strcmp(ic->iformat->name, "mpegts"))
            max_stream_analyze_duration = 7*AV_TIME_BASE;
    }


    if (ic->pb)
        av_log(ic, AV_LOG_DEBUG, "Before avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d nb_streams:%d\n",
               avio_tell(ic->pb), ic->pb->bytes_read, ic->pb->seek_count, ic->nb_streams);


    /* 读取文件的数据到AVPacket,解析出每个流对应的解码器,
       像flv文件,可能要读120个TAG */
    read_size = 0;


    for (;;) {


        int analyzed_all_streams;
        if (ff_check_interrupt(&ic->interrupt_callback)) {
            ret = AVERROR_EXIT;
            av_log(ic, AV_LOG_DEBUG, "interrupted\n");
            break;
        }


        /* check if one codec still needs to be handled */
        /* 需要分析出每一个流所应该匹配的解码器 */
        for (i = 0; i < ic->nb_streams; i++) {
            int fps_analyze_framecount = 20;


            st = ic->streams[i];
            if (!has_codec_parameters(st, NULL))
                break;
            /* If the timebase is coarse (like the usual millisecond precision
             * of mkv), we need to analyze more frames to reliably arrive at
             * the correct fps. */
            if (av_q2d(st->time_base) > 0.0005)
                fps_analyze_framecount *= 2;
            if (!tb_unreliable(st->internal->avctx))
                fps_analyze_framecount = 0;
            if (ic->fps_probe_size >= 0)
                fps_analyze_framecount = ic->fps_probe_size;
            if (st->disposition & AV_DISPOSITION_ATTACHED_PIC)
                fps_analyze_framecount = 0;
            /* variable fps and no guess at the real fps */
            if (!(st->r_frame_rate.num && st->avg_frame_rate.num) &&
                st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
                int count = (ic->iformat->flags & AVFMT_NOTIMESTAMPS) ?
                    st->info->codec_info_duration_fields/2 :
                    st->info->duration_count;
                if (count < fps_analyze_framecount)
                    break;
            }
            if (st->parser && st->parser->parser->split &&
                !st->internal->avctx->extradata)
                break;
            if (st->first_dts == AV_NOPTS_VALUE &&
                !(ic->iformat->flags & AVFMT_NOTIMESTAMPS) &&
                st->codec_info_nb_frames < ((st->disposition & AV_DISPOSITION_ATTACHED_PIC) ? 1 : ic->max_ts_probe) &&
                (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
                 st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))
                break;
        }
        analyzed_all_streams = 0;
        if (!missing_streams || !*missing_streams)
        if (i == ic->nb_streams) {
            analyzed_all_streams = 1;
            /* NOTE: If the format has no header, then we need to read some
             * packets to get most of the streams, so we cannot stop here. */
            if (!(ic->ctx_flags & AVFMTCTX_NOHEADER)) {
                /* If we found the info for all the codecs, we can stop. */
                ret = count;
                av_log(ic, AV_LOG_DEBUG, "All info found\n");
                flush_codecs = 0;
                break;
            }
        }
        /* We did not get all the codec info, but we read too much data. */
        if (read_size >= probesize) {
            ret = count;
            av_log(ic, AV_LOG_DEBUG,
                   "Probe buffer size limit of %"PRId64" bytes reached\n", probesize);
            for (i = 0; i < ic->nb_streams; i++)
                if (!ic->streams[i]->r_frame_rate.num &&
                    ic->streams[i]->info->duration_count <= 1 &&
                    ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
                    strcmp(ic->iformat->name, "image2"))
                    av_log(ic, AV_LOG_WARNING,
                           "Stream #%d: not enough frames to estimate rate; "
                           "consider increasing probesize\n", i);
            break;
        }




        /* NOTE: A new stream can be added there if no header in file
         * (AVFMTCTX_NOHEADER). */
        /* 从文件中读取数据包到AVPacket中,并进行分析 */
        ret = read_frame_internal(ic, &pkt1);
        if (ret == AVERROR(EAGAIN))
            continue;


        if (ret < 0) {
            /* EOF or error*/
            eof_reached = 1;
            break;
        }


        pkt = &pkt1;


        if (!(ic->flags & AVFMT_FLAG_NOBUFFER)) {
            /* 将pkt添加到ic->internal->packet_buffer的链表尾部(即ic->internal->packet_buffer_end)之后,*/
            ret = add_to_pktbuf(&ic->internal->packet_buffer, pkt,
                                &ic->internal->packet_buffer_end, 0);
            if (ret < 0)
                goto find_stream_info_err;
        }


        st = ic->streams[pkt->stream_index];
        if (!(st->disposition & AV_DISPOSITION_ATTACHED_PIC))
            read_size += pkt->size;


        avctx = st->internal->avctx;
        if (!st->internal->avctx_inited) {
            ret = avcodec_parameters_to_context(avctx, st->codecpar);
            if (ret < 0)
                goto find_stream_info_err;
            st->internal->avctx_inited = 1;
        }




#if FF_API_R_FRAME_RATE
        if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
            ff_rfps_add_frame(ic, st, pkt->dts);
#endif




        /* If still no information, we try to open the codec and to
         * decompress the frame. We try to avoid that in most cases as
         * it takes longer and uses more memory. For MPEG-4, we need to
         * decompress for QuickTime.
         *
         * If AV_CODEC_CAP_CHANNEL_CONF is set this will force decoding of at
         * least one frame of codec data, this makes sure the codec initializes
         * the channel configuration and does not only trust the values from
         * the container. */
        try_decode_frame(ic, st, pkt,
                         (options && i < orig_nb_streams) ? &options[i] : NULL);


        if (ic->flags & AVFMT_FLAG_NOBUFFER)
            av_packet_unref(pkt);


        st->codec_info_nb_frames++;
        count++;
    } // END OF for(;;)




    if (eof_reached) {
        int stream_index;
        for (stream_index = 0; stream_index < ic->nb_streams; stream_index++) {
            st = ic->streams[stream_index];
            avctx = st->internal->avctx;
            if (!has_codec_parameters(st, NULL)) {
                const AVCodec *codec = find_probe_decoder(ic, st, st->codecpar->codec_id);
                if (codec && !avctx->codec) {
                    if (avcodec_open2(avctx, codec, (options && stream_index < orig_nb_streams) ? &options[stream_index] : NULL) < 0)
                        av_log(ic, AV_LOG_WARNING,
                            "Failed to open codec in av_find_stream_info\n");
                }
            }


            // EOF already reached while reading the stream above.
            // So continue with reoordering DTS with whatever delay we have.
            if (ic->internal->packet_buffer && !has_decode_delay_been_guessed(st)) {
                update_dts_from_pts(ic, stream_index, ic->internal->packet_buffer);
            }
        }
    }


    if (flush_codecs) {
        AVPacket empty_pkt = { 0 };
        int err = 0;
        av_init_packet(&empty_pkt);


        for (i = 0; i < ic->nb_streams; i++) {


            st = ic->streams[i];


            /* flush the decoders */
            if (st->info->found_decoder == 1) {
                do {
                    err = try_decode_frame(ic, st, &empty_pkt,
                                            (options && i < orig_nb_streams)
                                            ? &options[i] : NULL);
                } while (err > 0 && !has_codec_parameters(st, NULL));


                if (err < 0) {
                    av_log(ic, AV_LOG_INFO,
                        "decoding for stream %d failed\n", st->index);
                }
            }
        }
    }


    // close codecs which were opened in try_decode_frame()
    for (i = 0; i < ic->nb_streams; i++) {
        st = ic->streams[i];
        avcodec_close(st->internal->avctx);
    }


    ff_rfps_calculate(ic);


    for (i = 0; i < ic->nb_streams; i++) {
        st = ic->streams[i];
        avctx = st->internal->avctx;


        if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) {
            if (avctx->codec_id == AV_CODEC_ID_RAWVIDEO && !avctx->codec_tag && !avctx->bits_per_coded_sample) {
                uint32_t tag= avcodec_pix_fmt_to_codec_tag(avctx->pix_fmt);
                if (avpriv_find_pix_fmt(avpriv_get_raw_pix_fmt_tags(), tag) == avctx->pix_fmt)
                    avctx->codec_tag= tag;
            }


            /* estimate average framerate if not set by demuxer */
            if (st->info->codec_info_duration_fields &&
                !st->avg_frame_rate.num &&
                st->info->codec_info_duration) {
                int best_fps      = 0;
                double best_error = 0.01;


                if (st->info->codec_info_duration        >= INT64_MAX / st->time_base.num / 2||
                    st->info->codec_info_duration_fields >= INT64_MAX / st->time_base.den ||
                    st->info->codec_info_duration        < 0)
                    continue;
                av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
                          st->info->codec_info_duration_fields * (int64_t) st->time_base.den,
                          st->info->codec_info_duration * 2 * (int64_t) st->time_base.num, 60000);


                /* Round guessed framerate to a "standard" framerate if it's
                 * within 1% of the original estimate. */
                for (j = 0; j < MAX_STD_TIMEBASES; j++) {
                    AVRational std_fps = { get_std_framerate(j), 12 * 1001 };
                    double error       = fabs(av_q2d(st->avg_frame_rate) /
                                              av_q2d(std_fps) - 1);


                    if (error < best_error) {
                        best_error = error;
                        best_fps   = std_fps.num;
                    }
                }
                if (best_fps)
                    av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
                              best_fps, 12 * 1001, INT_MAX);
            }


            if (!st->r_frame_rate.num) {
                if (    avctx->time_base.den * (int64_t) st->time_base.num
                    <= avctx->time_base.num * avctx->ticks_per_frame * (int64_t) st->time_base.den) {
                    st->r_frame_rate.num = avctx->time_base.den;
                    st->r_frame_rate.den = avctx->time_base.num * avctx->ticks_per_frame;
                } else {
                    st->r_frame_rate.num = st->time_base.den;
                    st->r_frame_rate.den = st->time_base.num;
                }
            }
            if (st->display_aspect_ratio.num && st->display_aspect_ratio.den) {
                AVRational hw_ratio = { avctx->height, avctx->width };
                st->sample_aspect_ratio = av_mul_q(st->display_aspect_ratio,
                                                   hw_ratio);
            }
        } else if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) {
            if (!avctx->bits_per_coded_sample)
                avctx->bits_per_coded_sample =
                    av_get_bits_per_sample(avctx->codec_id);
            // set stream disposition based on audio service type
            switch (avctx->audio_service_type) {
            case AV_AUDIO_SERVICE_TYPE_EFFECTS:
                st->disposition = AV_DISPOSITION_CLEAN_EFFECTS;
                break;
            case AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED:
                st->disposition = AV_DISPOSITION_VISUAL_IMPAIRED;
                break;
            case AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED:
                st->disposition = AV_DISPOSITION_HEARING_IMPAIRED;
                break;
            case AV_AUDIO_SERVICE_TYPE_COMMENTARY:
                st->disposition = AV_DISPOSITION_COMMENT;
                break;
            case AV_AUDIO_SERVICE_TYPE_KARAOKE:
                st->disposition = AV_DISPOSITION_KARAOKE;
                break;
            }
        }
    }


    if (probesize)
        estimate_timings(ic, old_offset);


    av_opt_set(ic, "skip_clear", "0", AV_OPT_SEARCH_CHILDREN);


    if (ret >= 0 && ic->nb_streams)
        /* We could not have all the codec parameters before EOF. */
        ret = -1;
    for (i = 0; i < ic->nb_streams; i++) {
        const char *errmsg;
        st = ic->streams[i];


        /* if no packet was ever seen, update context now for has_codec_parameters */
        if (!st->internal->avctx_inited) {
            if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
                st->codecpar->format == AV_SAMPLE_FMT_NONE)
                st->codecpar->format = st->internal->avctx->sample_fmt;
            ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar);
            if (ret < 0)
                goto find_stream_info_err;
        }
        if (!has_codec_parameters(st, &errmsg)) {
            char buf[256];
            avcodec_string(buf, sizeof(buf), st->internal->avctx, 0);
            av_log(ic, AV_LOG_WARNING,
                   "Could not find codec parameters for stream %d (%s): %s\n"
                   "Consider increasing the value for the 'analyzeduration' and 'probesize' options\n",
                   i, buf, errmsg);
        } else {
            ret = 0;
        }
    }


    compute_chapters_end(ic);


    /* update the stream parameters from the internal codec contexts */
    for (i = 0; i < ic->nb_streams; i++) {
        st = ic->streams[i];


        if (st->internal->avctx_inited) {
            int orig_w = st->codecpar->width;
            int orig_h = st->codecpar->height;
            ret = avcodec_parameters_from_context(st->codecpar, st->internal->avctx);
            if (ret < 0)
                goto find_stream_info_err;
            // The decoder might reduce the video size by the lowres factor.
            if (av_codec_get_lowres(st->internal->avctx) && orig_w) {
                st->codecpar->width = orig_w;
                st->codecpar->height = orig_h;
            }
        }


        st->internal->avctx_inited = 0;
    }


find_stream_info_err:
    for (i = 0; i < ic->nb_streams; i++) {
        st = ic->streams[i];
        if (st->info)
            av_freep(&st->info->duration_error);
        av_freep(&ic->streams[i]->info);
    }
    if (ic->pb)
        av_log(ic, AV_LOG_DEBUG, "After avformat_find_stream_info() pos: %"PRId64" bytes read:%"PRId64" seeks:%d frames:%d\n",
               avio_tell(ic->pb), ic->pb->bytes_read, ic->pb->seek_count, count);
    return ret;
}




3.3 open_output_file()
static int open_output_file(OptionsContext *o, const char *filename)
{
    AVFormatContext *oc;
    int i, j, err;
    AVOutputFormat *file_oformat;
    OutputFile *of;
    OutputStream *ost;
    InputStream  *ist;
    AVDictionary *unused_opts = NULL;
    AVDictionaryEntry *e = NULL;




    /* 初始化 OutputFile *of */
    GROW_ARRAY(output_files, nb_output_files);
    of = av_mallocz(sizeof(*of));
    if (!of)
        exit_program(1);
    output_files[nb_output_files - 1] = of;


    of->ost_index      = nb_output_streams;
    of->recording_time = o->recording_time;
    of->start_time     = o->start_time;
    of->limit_filesize = o->limit_filesize;
    of->shortest       = o->shortest;
    av_dict_copy(&of->opts, o->g->format_opts, 0);


    if (!strcmp(filename, "-"))
        filename = "pipe:";


    /* 初始化 AVFormatContext *oc */
    err = avformat_alloc_output_context2(&oc, NULL, o->format, filename);
    if (!oc) {
        print_error(filename, err);
        exit_program(1);
    }


    of->ctx = oc;
    if (o->recording_time != INT64_MAX)
        oc->duration = o->recording_time;


    file_oformat= oc->oformat;
    oc->interrupt_callback = int_cb;






    /* ffserver seeking with date=... needs a date reference */
    if (!strcmp(file_oformat->name, "ffm") &&
        av_strstart(filename, "http:", NULL)) {
        int err = parse_option(o, "metadata", "creation_time=now", options);
        if (err < 0) {
            print_error(filename, err);
            exit_program(1);
        }
    }


    if (!strcmp(file_oformat->name, "ffm") && !override_ffserver &&
        av_strstart(filename, "http:", NULL)) {
       ...
    } else if (!o->nb_stream_maps) {
        char *subtitle_codec_name = NULL;
        /* pick the "best" stream of each type */


        /* video: highest resolution */
        if (!o->video_disable && av_guess_codec(oc->oformat, NULL, filename, NULL, AVMEDIA_TYPE_VIDEO) != AV_CODEC_ID_NONE) {
            int area = 0, idx = -1;
            int qcr = avformat_query_codec(oc->oformat, oc->oformat->video_codec, 0);
            for (i = 0; i < nb_input_streams; i++) {
                int new_area;
                ist = input_streams[i];
                new_area = ist->st->codecpar->width * ist->st->codecpar->height + 100000000*!!ist->st->codec_info_nb_frames;
                if((qcr!=MKTAG('A', 'P', 'I', 'C')) && (ist->st->disposition & AV_DISPOSITION_ATTACHED_PIC))
                    new_area = 1;
                if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
                    new_area > area) {
                    if((qcr==MKTAG('A', 'P', 'I', 'C')) && !(ist->st->disposition & AV_DISPOSITION_ATTACHED_PIC))
                        continue;
                    area = new_area;
                    idx = i;
                }
            }
            /* 初始化输出视频流的结构体*/
            if (idx >= 0)
                new_video_stream(o, oc, idx);
        }


        /* audio: most channels */
        if (!o->audio_disable && av_guess_codec(oc->oformat, NULL, filename, NULL, AVMEDIA_TYPE_AUDIO) != AV_CODEC_ID_NONE) {
            int best_score = 0, idx = -1;
            for (i = 0; i < nb_input_streams; i++) {
                int score;
                ist = input_streams[i];
                score = ist->st->codecpar->channels + 100000000*!!ist->st->codec_info_nb_frames;
                if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
                    score > best_score) {
                    best_score = score;
                    idx = i;
                }
            }
           /* 初始化输出音频流的结构体*/
            if (idx >= 0)
                new_audio_stream(o, oc, idx);
        }
    }


#if FF_API_LAVF_AVCTX
    for (i = nb_output_streams - oc->nb_streams; i < nb_output_streams; i++) { //for all streams of this output file
        AVDictionaryEntry *e;
        ost = output_streams[i];


        if ((ost->stream_copy || ost->attachment_filename)
            && (e = av_dict_get(o->g->codec_opts, "flags", NULL, AV_DICT_IGNORE_SUFFIX))
            && (!e->key[5] || check_stream_specifier(oc, ost->st, e->key+6)))
            if (av_opt_set(ost->st->codec, "flags", e->value, 0) < 0)
                exit_program(1);
    }
#endif


    if (!oc->nb_streams && !(oc->oformat->flags & AVFMT_NOSTREAMS)) {
        av_dump_format(oc, nb_output_files - 1, oc->filename, 1);
        av_log(NULL, AV_LOG_ERROR, "Output file #%d does not contain any stream\n", nb_output_files - 1);
        exit_program(1);
    }


    /* check if all codec options have been used */
    unused_opts = strip_specifiers(o->g->codec_opts);
    for (i = of->ost_index; i < nb_output_streams; i++) {
        e = NULL;
        while ((e = av_dict_get(output_streams[i]->encoder_opts, "", e,
                                AV_DICT_IGNORE_SUFFIX)))
            av_dict_set(&unused_opts, e->key, NULL, 0);
    }


    e = NULL;
    while ((e = av_dict_get(unused_opts, "", e, AV_DICT_IGNORE_SUFFIX))) {
        const AVClass *class = avcodec_get_class();
        const AVOption *option = av_opt_find(&class, e->key, NULL, 0,
                                             AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
        const AVClass *fclass = avformat_get_class();
        const AVOption *foption = av_opt_find(&fclass, e->key, NULL, 0,
                                              AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ);
        if (!option || foption)
            continue;




        if (!(option->flags & AV_OPT_FLAG_ENCODING_PARAM)) {
            av_log(NULL, AV_LOG_ERROR, "Codec AVOption %s (%s) specified for "
                   "output file #%d (%s) is not an encoding option.\n", e->key,
                   option->help ? option->help : "", nb_output_files - 1,
                   filename);
            exit_program(1);
        }


        // gop_timecode is injected by generic code but not always used
        if (!strcmp(e->key, "gop_timecode"))
            continue;


        av_log(NULL, AV_LOG_WARNING, "Codec AVOption %s (%s) specified for "
               "output file #%d (%s) has not been used for any stream. The most "
               "likely reason is either wrong type (e.g. a video option with "
               "no video streams) or that it is a private option of some encoder "
               "which was not actually used for any stream.\n", e->key,
               option->help ? option->help : "", nb_output_files - 1, filename);
    }
    av_dict_free(&unused_opts);


    /* set the decoding_needed flags and create simple filtergraphs */
    for (i = of->ost_index; i < nb_output_streams; i++) {
        OutputStream *ost = output_streams[i];


        if (ost->encoding_needed && ost->source_index >= 0) {
            InputStream *ist = input_streams[ost->source_index];
            ist->decoding_needed |= DECODING_FOR_OST;


            if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
                ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
                err = init_simple_filtergraph(ist, ost);
                if (err < 0) {
                    av_log(NULL, AV_LOG_ERROR,
                           "Error initializing a simple filtergraph between streams "
                           "%d:%d->%d:%d\n", ist->file_index, ost->source_index,
                           nb_output_files - 1, ost->st->index);
                    exit_program(1);
                }
            }
        }
    }


    /* check filename in case of an image number is expected */
    if (oc->oformat->flags & AVFMT_NEEDNUMBER) {
        if (!av_filename_number_test(oc->filename)) {
            print_error(oc->filename, AVERROR(EINVAL));
            exit_program(1);
        }
    }


    if (!(oc->oformat->flags & AVFMT_NOSTREAMS) && !input_stream_potentially_available) {
        av_log(NULL, AV_LOG_ERROR,
               "No input streams but output needs an input stream\n");
        exit_program(1);
    }


    if (!(oc->oformat->flags & AVFMT_NOFILE)) {
        /* test if it already exists to avoid losing precious files */
        assert_file_overwrite(filename);


        /* open the file */
        if ((err = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE,
                              &oc->interrupt_callback,
                              &of->opts)) < 0) {
            print_error(filename, err);
            exit_program(1);
        }
    } else if (strcmp(oc->oformat->name, "image2")==0 && !av_filename_number_test(filename))
        assert_file_overwrite(filename);


    if (o->mux_preload) {
        av_dict_set_int(&of->opts, "preload", o->mux_preload*AV_TIME_BASE, 0);
    }
    oc->max_delay = (int)(o->mux_max_delay * AV_TIME_BASE);


    /* copy metadata */
    for (i = 0; i < o->nb_metadata_map; i++) {
        char *p;
        int in_file_index = strtol(o->metadata_map[i].u.str, &p, 0);


        if (in_file_index >= nb_input_files) {
            av_log(NULL, AV_LOG_FATAL, "Invalid input file index %d while processing metadata maps\n", in_file_index);
            exit_program(1);
        }
        copy_metadata(o->metadata_map[i].specifier, *p ? p + 1 : p, oc,
                      in_file_index >= 0 ?
                      input_files[in_file_index]->ctx : NULL, o);
    }


    /* copy chapters */
    if (o->chapters_input_file >= nb_input_files) {
        if (o->chapters_input_file == INT_MAX) {
            /* copy chapters from the first input file that has them*/
            o->chapters_input_file = -1;
            for (i = 0; i < nb_input_files; i++)
                if (input_files[i]->ctx->nb_chapters) {
                    o->chapters_input_file = i;
                    break;
                }
        } else {
            av_log(NULL, AV_LOG_FATAL, "Invalid input file index %d in chapter mapping.\n",
                   o->chapters_input_file);
            exit_program(1);
        }
    }
    if (o->chapters_input_file >= 0)
        copy_chapters(input_files[o->chapters_input_file], of,
                      !o->metadata_chapters_manual);


    /* copy global metadata by default */
    if (!o->metadata_global_manual && nb_input_files){
        av_dict_copy(&oc->metadata, input_files[0]->ctx->metadata,
                     AV_DICT_DONT_OVERWRITE);
        if(o->recording_time != INT64_MAX)
            av_dict_set(&oc->metadata, "duration", NULL, 0);
        av_dict_set(&oc->metadata, "creation_time", NULL, 0);
    }
    if (!o->metadata_streams_manual)
        for (i = of->ost_index; i < nb_output_streams; i++) {
            InputStream *ist;
            if (output_streams[i]->source_index < 0)         /* this is true e.g. for attached files */
                continue;
            ist = input_streams[output_streams[i]->source_index];
            av_dict_copy(&output_streams[i]->st->metadata, ist->st->metadata, AV_DICT_DONT_OVERWRITE);
            if (!output_streams[i]->stream_copy) {
                av_dict_set(&output_streams[i]->st->metadata, "encoder", NULL, 0);
                if (ist->autorotate)
                    av_dict_set(&output_streams[i]->st->metadata, "rotate", NULL, 0);
            }
        }


    /* process manually set programs */
    for (i = 0; i < o->nb_program; i++) {
        const char *p = o->program[i].u.str;
        int progid = i+1;
        AVProgram *program;


        while(*p) {
            const char *p2 = av_get_token(&p, ":");
            const char *to_dealloc = p2;
            char *key;
            if (!p2)
                break;


            if(*p) p++;


            key = av_get_token(&p2, "=");
            if (!key || !*p2) {
                av_freep(&to_dealloc);
                av_freep(&key);
                break;
            }
            p2++;


            if (!strcmp(key, "program_num"))
                progid = strtol(p2, NULL, 0);
            av_freep(&to_dealloc);
            av_freep(&key);
        }


        program = av_new_program(oc, progid);


        p = o->program[i].u.str;
        while(*p) {
            const char *p2 = av_get_token(&p, ":");
            const char *to_dealloc = p2;
            char *key;
            if (!p2)
                break;
            if(*p) p++;


            key = av_get_token(&p2, "=");
            if (!key) {
                av_log(NULL, AV_LOG_FATAL,
                       "No '=' character in program string %s.\n",
                       p2);
                exit_program(1);
            }
            if (!*p2)
                exit_program(1);
            p2++;


            if (!strcmp(key, "title")) {
                av_dict_set(&program->metadata, "title", p2, 0);
            } else if (!strcmp(key, "program_num")) {
            } else if (!strcmp(key, "st")) {
                int st_num = strtol(p2, NULL, 0);
                av_program_add_stream_index(oc, progid, st_num);
            } else {
                av_log(NULL, AV_LOG_FATAL, "Unknown program key %s.\n", key);
                exit_program(1);
            }
            av_freep(&to_dealloc);
            av_freep(&key);
        }
    }


    /* process manually set metadata */
    for (i = 0; i < o->nb_metadata; i++) {
        AVDictionary **m;
        char type, *val;
        const char *stream_spec;
        int index = 0, j, ret = 0;


        val = strchr(o->metadata[i].u.str, '=');
        if (!val) {
            av_log(NULL, AV_LOG_FATAL, "No '=' character in metadata string %s.\n",
                   o->metadata[i].u.str);
            exit_program(1);
        }
        *val++ = 0;


        parse_meta_type(o->metadata[i].specifier, &type, &index, &stream_spec);
        if (type == 's') {
            for (j = 0; j < oc->nb_streams; j++) {
                ost = output_streams[nb_output_streams - oc->nb_streams + j];
                if ((ret = check_stream_specifier(oc, oc->streams[j], stream_spec)) > 0) {
                    av_dict_set(&oc->streams[j]->metadata, o->metadata[i].u.str, *val ? val : NULL, 0);
                    if (!strcmp(o->metadata[i].u.str, "rotate")) {
                        ost->rotate_overridden = 1;
                    }
                } else if (ret < 0)
                    exit_program(1);
            }
        }
        else {
            switch (type) {
            case 'g':
                m = &oc->metadata;
                break;
            case 'c':
                if (index < 0 || index >= oc->nb_chapters) {
                    av_log(NULL, AV_LOG_FATAL, "Invalid chapter index %d in metadata specifier.\n", index);
                    exit_program(1);
                }
                m = &oc->chapters[index]->metadata;
                break;
            case 'p':
                if (index < 0 || index >= oc->nb_programs) {
                    av_log(NULL, AV_LOG_FATAL, "Invalid program index %d in metadata specifier.\n", index);
                    exit_program(1);
                }
                m = &oc->programs[index]->metadata;
                break;
            default:
                av_log(NULL, AV_LOG_FATAL, "Invalid metadata specifier %s.\n", o->metadata[i].specifier);
                exit_program(1);
            }
            av_dict_set(m, o->metadata[i].u.str, *val ? val : NULL, 0);
        }
    }


    return 0;
}
 

ma@ubuntu:~/Downloads/torchvision$ python3 setup.py install setup.py:10: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html from pkg_resources import DistributionNotFound, get_distribution, parse_version Building wheel torchvision-0.15.1 Compiling extensions with following flags: FORCE_CUDA: False DEBUG: False TORCHVISION_USE_PNG: True TORCHVISION_USE_JPEG: True TORCHVISION_USE_NVJPEG: True TORCHVISION_USE_FFMPEG: True TORCHVISION_USE_VIDEO_CODEC: True NVCC_FLAGS: Found PNG library Building torchvision with PNG image support libpng version: 1.6.37 libpng include path: /usr/include/libpng16 Running build on conda-build: False Running build on conda: False Building torchvision with JPEG image support Building torchvision without NVJPEG image support ffmpeg: symbol lookup error: ffmpeg: undefined symbol: avio_protocol_get_class, version LIBAVFORMAT_58 Building torchvision without ffmpeg support Error fetching ffmpeg version, ignoring ffmpeg. Building torchvision without ffmpeg support Building torchvision without video codec support running install /home/ma/.local/lib/python3.8/site-packages/setuptools/_distutils/cmd.py:66: SetuptoolsDeprecationWarning: setup.py install is deprecated. !! ******************************************************************************** Please avoid running ``setup.py`` directly. Instead, use pypa/build, pypa/installer or other standards-based tools. See https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html for details. ******************************************************************************** !! self.initialize_options() /home/ma/.local/lib/python3.8/site-packages/setuptools/_distutils/cmd.py:66: EasyInstallDeprecationWarning: easy_install command is deprecated. !! ******************************************************************************** Please avoid running ``setup.py`` and ``easy_install``. Instead, use pypa/build, pypa/installer or other standards-based tools. See https://github.com/pypa/setuptools/issues/917 for details. ******************************************************************************** !! self.initialize_options() error: can't create or remove files in install directory The following error occurred while trying to add or remove files in the installation directory: [Errno 13] Permission denied: '/usr/lib/python3.8/site-packages' The installation directory you specified (via --install-dir, --prefix, or the distutils default setting) was: /usr/lib/python3.8/site-packages/ This directory does not currently exist. Please create it and try again, or choose a different installation directory (using the -d or --install-dir option).
最新发布
06-04
<think>我们正在解决两个问题:1.在运行`pythonsetup.pyinstall`安装torchvision时遇到权限问题(Permissiondenied)2.安装过程中缺少依赖(libpng,ffmpeg)导致的错误解决方案:一、解决权限问题当我们使用`pythonsetup.pyinstall`时,它默认会尝试将包安装到系统Python的site-packages目录(例如`/usr/local/lib/python3.X/site-packages`),这需要root权限。如果没有使用sudo,就会遇到Permissiondenied错误。有两种方法可以解决权限问题:方法1:使用sudo(不推荐,因为可能会影响系统Python环境)```bashsudopythonsetup.pyinstall```方法2:推荐使用虚拟环境(virtualenvironment)或者用户安装(--user)选项,这样就不需要root权限。使用虚拟环境(推荐):```bash#创建虚拟环境python-mvenvmyenvsourcemyenv/bin/activate#Linux/Mac#或者Windows:myenv\Scripts\activate#在虚拟环境中安装torchvisionpythonsetup.pyinstall```或者使用`--user`选项安装到用户目录:```bashpythonsetup.pyinstall--user```二、解决依赖问题(libpng,ffmpeg等)在编译torchvision时,需要确保系统已经安装了必要的开发库。这些库包括:-libjpeg-dev-libpng-dev-ffmpeg(用于视频支持)安装这些依赖的方法因操作系统而异。对于Ubuntu/Debian系统:```bashsudoapt-getupdatesudoapt-getinstalllibjpeg-devlibpng-devlibavcodec-devlibavformat-devlibswscale-dev#如果需要ffmpeg,可以安装sudoapt-getinstallffmpeg```对于CentOS/RHEL系统:```bashsudoyuminstalllibjpeg-devellibpng-develsudoyuminstallffmpegffmpeg-devel```对于macOS(使用Homebrew):```bashbrewinstalljpeglibpngffmpeg```安装完这些依赖后,重新运行安装命令。三、重新安装torchvision确保在正确解决了权限问题和依赖问题后,重新安装:在虚拟环境中(或使用--user):```bashpythonsetup.pyinstall```或者使用pip安装(如果你不想从源码安装,也可以直接使用pip安装预编译的版本,但有时预编译版本可能不包含某些功能):```bashpipinstalltorchvision```四、验证安装安装完成后,在Python中尝试导入torchvision并检查图像操作是否正常:```pythonimporttorchvisionfromtorchvision.ioimportread_image#尝试读取一张图片(需要有一张测试图片,比如'test.jpg')img=read_image('test.jpg')print(img.shape)```如果没有报错,说明安装成功。注意:有时torchvision的版本需要和PyTorch匹配,请确保安装的torchvision版本与已安装的PyTorch版本兼容。可以在torchvision的GitHub仓库的releasenotes中查看对应关系。如果安装后仍然出现关于libjpeg/libpng的警告,可能是因为在编译时没有正确链接到这些库。可以尝试设置环境变量,指定库的路径,例如:```bashexportPKG_CONFIG_PATH="/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH"```然后再重新运行安装命令。五、关于ffmpeg的说明如果视频功能不是必需的,可以在编译torchvision时禁用视频支持,这样可以跳过ffmpeg的依赖。在运行setup.py时添加环境变量:```bashTORCHVISION_USE_FFMPEG=0pythonsetup.pyinstall```但是,如果视频功能是必需的,请确保ffmpeg及其开发库已经安装。总结步骤:1.创建虚拟环境(推荐,避免权限问题)2.安装系统依赖(libjpeg,libpng,ffmpeg等)3.设置环境变量(如果需要,确保编译器能够找到依赖库)4.运行安装命令(使用虚拟环境,不需要sudo)如果问题仍然存在,请提供具体的错误信息以便进一步分析。---§§相关问题§§1.如何检查系统中是否已安装`libjpeg`和`libpng`?2.在Windows系统上如何解决`torchvision`的安装权限和依赖问题?3.安装`torchvision`时如何选择与PyTorch版本兼容的版本?4.如何查看`torchvision`编译时的选项(如是否启用了ffmpeg)?5.在Docker容器中安装`torchvision`需要注意哪些依赖问题?</think> ### 解决 Torchvision 安装中的权限和依赖问题 您遇到的权限问题(`Permission denied`)和依赖问题(`libpng`, `ffmpeg`错误)是 Torchvision 编译安装时的常见问题。以下是系统的解决方案: #### 一、解决权限问题 1. **使用虚拟环境安装**(推荐方案) 创建独立的 Python 环境避免系统权限问题: ```bash python -m venv torchvision-env # 创建虚拟环境 source torchvision-env/bin/activate # 激活环境(Linux/Mac) # Windows: torchvision-env\Scripts\activate pip install --upgrade pip setuptools wheel pip install torchvision --no-cache-dir # 优先尝试安装二进制包 ``` 2. **使用用户级安装选项** 不需要 sudo 权限: ```bash pip install --user torchvision # 安装到用户目录 ``` 3. **修复目录权限** 如果您坚持从源码编译: ```bash sudo chown -R $USER /usr/local # Linux/Mac # 或者指定安装位置 python setup.py install --prefix ~/.local # 用户目录 ``` #### 二、解决依赖问题(libpng, ffmpeg等) 1. **安装编译依赖库**: ```bash # Ubuntu/Debian sudo apt-get install libjpeg-dev libpng-dev ffmpeg libavcodec-dev libavformat-dev libswscale-dev # CentOS/RHEL sudo yum install libjpeg-devel libpng-devel ffmpeg ffmpeg-devel # macOS (使用Homebrew) brew install jpeg libpng ffmpeg pkg-config ``` 2. **设置环境变量**: 确保编译器能找到依赖库: ```bash export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH" ``` 3. **强制启用视频支持**(解决 ffmpeg 错误): ```bash # 从源码安装时指定 TORCHVISION_USE_FFMPEG=1 python setup.py install ``` 4. **使用 conda 管理依赖**(解决库冲突): ```bash conda create -n torchvision-env python=3.8 conda activate torchvision-env conda install pytorch torchvision cudatoolkit=11.3 -c pytorch ``` #### 三、完整解决流程 1. **创建虚拟环境** ```bash python -m venv tv-env && source tv-env/bin/activate ``` 2. **安装系统依赖** ```bash sudo apt-get install -y libjpeg-dev libpng-dev ffmpeg ``` 3. **配置编译环境** ```bash export C_INCLUDE_PATH="/usr/local/include:$C_INCLUDE_PATH" export LIBRARY_PATH="/usr/local/lib:$LIBRARY_PATH" ``` 4. **安装 Torchvision** ```bash pip install --no-cache-dir torchvision # 首选二进制安装 ``` 如果失败,从源码编译: ```bash git clone https://github.com/pytorch/vision.git cd vision TORCHVISION_USE_FFMPEG=1 python setup.py install ``` #### 四、验证安装 ```python from torchvision.io import read_image, read_video # 测试图像支持 img = read_image("test.jpg") print(f"Image loaded: {img.shape}") # 测试视频支持 video, _, _ = read_video("test.mp4", pts_unit='sec') print(f"Video loaded: {video.shape}") ``` > **重要提示**:如果仍然报错,尝试使用 Docker 官方镜像避免环境问题: > ```bash > docker run -it pytorch/pytorch:latest bash > pip install torchvision > ``` #### 五、疑难排查 1. **定位缺失依赖**: ```bash ldd build/lib.linux-x86_64-3.8/torchvision/_C.so | grep "not found" ``` 2. **查看详细编译日志**: ```bash python setup.py install --verbose 2>&1 | tee build.log ``` 3. **降级版本匹配PyTorch**: ```python # 参考版本兼容表 https://github.com/pytorch/vision#installation pip install torchvision==0.11.1+cu113 -f https://download.pytorch.org/whl/cu113/torch_stable.html ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北雨南萍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值