当前位置: 首页 > news >正文

做美食网站的需求新闻发布平台有哪些

做美食网站的需求,新闻发布平台有哪些,男生女生做污事网站 localhost,服装网站建设图stream_open 讲解 // 定义一个静态函数用于初始化并返回VideoState结构体指针,用于管理播放状态 static VideoState* stream_open(const char* filename, AVInputFormat* iformat) {VideoState* is; // 创建VideoState结构体指针// 分配内存并初始化VideoState结构…

stream_open 讲解

// 定义一个静态函数用于初始化并返回VideoState结构体指针,用于管理播放状态
static VideoState* stream_open(const char* filename, AVInputFormat* iformat) {VideoState* is; // 创建VideoState结构体指针// 分配内存并初始化VideoState结构体,若分配失败则直接返回NULLis = av_mallocz(sizeof(VideoState));if (!is)return NULL;// 复制输入文件名到VideoState结构体中,失败则跳转到fail标签释放资源is->filename = av_strdup(filename);//这是FFmpeg提供的一个便捷函数,用于创建一个字符串的副本,并且自动管理内存分配。它的行为类似于C标准库中的 strdup 函数,但它是FFmpeg专为视频处理等多媒体应用设计的,可能在内部做了某些特定于库的优化或错误处理。该函数接受一个字符串指针作为参数,返回一个新的分配的内存空间中的字符串副本。如果内存分配失败,它将返回 NULL。if (!is->filename)goto fail;// 设置输入格式和视频初始显示位置is->iformat = iformat;is->ytop = 0;is->xleft = 0;// 初始化帧队列,分别为视频帧、字幕帧、音频样本队列,失败则跳转到failif (frame_queue_init(&is->pictq, &is->videoq, VIDEO_PICTURE_QUEUE_SIZE, 1) < 0)goto fail;if (frame_queue_init(&is->subpq, &is->subtitleq, SUBPICTURE_QUEUE_SIZE, 0) < 0)goto fail;if (frame_queue_init(&is->sampq, &is->audioq, SAMPLE_QUEUE_SIZE, 1) < 0)goto fail;// 初始化数据包队列,包括视频、音频、字幕数据包队列,任一失败则跳转到failif (packet_queue_init(&is->videoq) < 0 ||packet_queue_init(&is->audioq) < 0 ||packet_queue_init(&is->subtitleq) < 0)goto fail;// 创建条件变量,用于线程同步,失败则记录错误并跳转到failif (!(is->continue_read_thread = SDL_CreateCond())) {av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());goto fail;}// 初始化时钟,分别与视频、音频和外部时钟序列关联init_clock(&is->vidclk, &is->videoq.serial);init_clock(&is->audclk, &is->audioq.serial);init_clock(&is->extclk, &is->extclk.serial);// 设置音频时钟序列初值,及音量处理逻辑is->audio_clock_serial = -1;if (startup_volume < 0)av_log(NULL, AV_LOG_WARNING, "-volume=%d < 0, setting to 0\n", startup_volume);if (startup_volume > 100)av_log(NULL, AV_LOG_WARNING, "-volume=%d > 100, setting to 100\n", startup_volume);startup_volume = av_clip(startup_volume, 0, 100);startup_volume = av_clip(SDL_MIX_MAXVOLUME * startup_volume / 100, 0, SDL_MIX_MAXVOLUME);is->audio_volume = startup_volume;is->muted = 0;is->av_sync_type = av_sync_type;// 创建读取线程,负责读取媒体数据,失败则记录错误并跳转到failis->read_tid = SDL_CreateThread(read_thread, "read_thread", is);if (!is->read_tid) {av_log(NULL, AV_LOG_FATAL, "SDL_CreateThread(): %s\n", SDL_GetError());goto fail;}// 若一切顺利,返回VideoState结构体指针return is;fail:// 清理资源并返回NULLstream_close(is);return NULL;
}

frame_queue_init 讲解

/*** 初始化帧队列结构体FrameQueue,包括线程同步对象和帧缓冲区的分配。** @param f          FrameQueue结构体指针,待初始化的帧队列对象。* @param pktq       PacketQueue结构体指针,关联的包队列,用于接收解码前的数据包。* @param max_size   帧队列的最大容量。* @param keep_last 是否保留队列中的最后一个帧,即使队列已满。* @return 初始化成功返回0,失败返回负的AVERROR代码。*/
static int frame_queue_init(FrameQueue* f, PacketQueue* pktq, int max_size, int keep_last)
{int i; // 用于循环计数// 首先,使用memset清除f所指向的内存,确保所有字段初始化为0memset(f, 0, sizeof(FrameQueue));// 创建互斥锁,用于保护队列访问的线程安全if (!(f->mutex = SDL_CreateMutex())) {// 如果创建失败,记录致命错误并返回ENOMEMav_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());return AVERROR(ENOMEM);}// 创建条件变量,用于线程间的同步,例如在队列为空时等待新帧的到来if (!(f->cond = SDL_CreateCond())) {av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());return AVERROR(ENOMEM);}// 设置队列的关联数据包队列、最大容量和是否保留最后一个帧的标志f->pktq = pktq;// 确保最大容量不超过预设的最大值FRAME_QUEUE_SIZEf->max_size = FFMIN(max_size, FRAME_QUEUE_SIZE);f->keep_last = !!keep_last; // 转换为布尔值,确保0或非0逻辑正确// 遍历至max_size,为队列中的每个槽位分配AVFrame结构体for (i = 0; i < f->max_size; i++) {// 分配帧缓冲,用于存储解码后的视频或音频帧if (!(f->queue[i].frame = av_frame_alloc())) {// 分配失败,返回ENOMEM错误return AVERROR(ENOMEM);}}// 所有初始化操作成功完成,返回0return 0;
}

packet_queue_init 讲解

/*** 初始化数据包队列处理结构体PacketQueue,包括创建必要的线程同步对象。** @param q 指向PacketQueue结构体的指针,待初始化的数据包队列对象。* @return 初始化成功返回0,失败返回负的AVERROR代码。*/
static int packet_queue_init(PacketQueue* q)
{// 使用memset函数清零q指向的内存区域,确保PacketQueue的所有成员初始化为0memset(q, 0, sizeof(PacketQueue));// 创建一个互斥锁(mutex),用于保护队列的并发访问,确保线程安全q->mutex = SDL_CreateMutex();if (!q->mutex) {// 如果创建互斥锁失败,记录一个致命错误并返回ENOMEM错误码av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());return AVERROR(ENOMEM);}// 创建一个条件变量(cond),用于线程间的同步,例如在队列为空时挂起等待新数据包的到来q->cond = SDL_CreateCond();if (!q->cond) {// 如果创建条件变量失败,同样记录致命错误并返回ENOMEMav_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());return AVERROR(ENOMEM);}// 初始化队列的abort_request标志为1,表明开始时没有请求中止操作// (这个标志通常会在队列的生命周期中用来请求停止队列的操作,如用户请求退出)q->abort_request = 1;// 成功完成所有初始化步骤,返回0return 0;
}

read_thread 讲解

/* 这个线程从磁盘或网络获取流数据 */
static int read_thread(void* arg)
{// 获取传入的参数,VideoState 结构体VideoState* is = arg;// AVFormatContext 结构体指针,用于存储媒体文件格式信息AVFormatContext* ic = NULL;int err, i, ret;// 存储流索引的数组,初始化为-1int st_index[AVMEDIA_TYPE_NB];// AVPacket 用于存储解码前的数据包AVPacket pkt1, *pkt = &pkt1;int64_t stream_start_time;int pkt_in_play_range = 0;AVDictionaryEntry* t;// SDL 互斥锁,用于线程同步SDL_mutex* wait_mutex = SDL_CreateMutex();int scan_all_pmts_set = 0;int64_t pkt_ts;// 如果无法创建互斥锁,记录错误并返回内存不足错误if (!wait_mutex) {av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());ret = AVERROR(ENOMEM);goto fail;}// 清零 st_index 数组并将其值设为 -1。 st_index 数组通常用于存储流索引。memset(st_index, -1, sizeof(st_index));// 初始化视频、音频和字幕流索引,分别将它们的最后使用和当前使用的流索引设置为 -1。is->last_video_stream = is->video_stream = -1;is->last_audio_stream = is->audio_stream = -1;is->last_subtitle_stream = is->subtitle_stream = -1;// 初始化 eof 标志,表示流未到达末尾。is->eof = 0;// 分配 AVFormatContext 结构体ic = avformat_alloc_context();if (!ic) {av_log(NULL, AV_LOG_FATAL, "Could not allocate context.\n");ret = AVERROR(ENOMEM);goto fail;}// 设置中断回调函数ic->interrupt_callback.callback = decode_interrupt_cb;ic->interrupt_callback.opaque = is;/*这行代码将 AVFormatContext 的 interrupt_callback 结构体中的 callback 成员设置为 decode_interrupt_cb 函数指针。decode_interrupt_cb 是一个用户定义的回调函数,用于在特定条件下中断解码过程,例如用户请求停止解码或者出现某些错误条件。这个回调函数的签名通常是 int (*callback)(void*),返回值是 int 类型,参数是一个 void* 类型的指针。ic->interrupt_callback.opaque = is;:这行代码将 AVFormatContext 的 interrupt_callback 结构体中的 opaque 成员设置为 is,通常是一个指向用户自定义数据结构的指针。opaque 成员将作为参数传递给 decode_interrupt_cb 回调函数,允许回调函数访问特定的上下文数据(如当前的解码状态)。*/// 如果没有设置 scan_all_pmts 选项,则设置它if (!av_dict_get(format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) {av_dict_set(&format_opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE);scan_all_pmts_set = 1;}// 打开输入文件err = avformat_open_input(&ic, is->filename, is->iformat, &format_opts);if (err < 0) {print_error(is->filename, err);ret = -1;goto fail;}// 如果设置了 scan_all_pmts 选项,则删除它if (scan_all_pmts_set)av_dict_set(&format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE);// 检查是否有未找到的选项if ((t = av_dict_get(format_opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {av_log(NULL, AV_LOG_ERROR, "Option %s not found.\n", t->key);ret = AVERROR_OPTION_NOT_FOUND;goto fail;}is->ic = ic;// 如果设置了 genpts 选项,则设置 AVFormatContext 的标志if (genpts)ic->flags |= AVFMT_FLAG_GENPTS;// 注入全局侧数据av_format_inject_global_side_data(ic);// 如果设置了 find_stream_info 选项,查找流信息if (find_stream_info) {AVDictionary** opts = setup_find_stream_info_opts(ic, codec_opts);int orig_nb_streams = ic->nb_streams;err = avformat_find_stream_info(ic, opts);for (i = 0; i < orig_nb_streams; i++)av_dict_free(&opts[i]);av_freep(&opts);if (err < 0) {av_log(NULL, AV_LOG_WARNING,"%s: could not find codec parameters\n", is->filename);ret = -1;goto fail;}}/*如果 find_stream_info 为真,则调用 setup_find_stream_info_opts 函数设置流信息查找选项,并返回一个 AVDictionary** 类型的指针数组 opts。获取原始流数量 orig_nb_streams,即打开文件后的流数量。调用 avformat_find_stream_info 函数查找流信息,并传入查找选项 opts。在查找完成后,释放 opts 数组中每个元素对应的 AVDictionary 结构体,并释放 opts 数组本身。如果查找失败(err < 0),记录警告日志,并设置 ret 为 -1,然后跳转到 fail 标签处执行清理工作。这段代码的作用是确保媒体文件的流信息被正确解析和初始化,以便后续的播放或处理操作能够正确进行。*/// 如果存在 IO 上下文,重置 eof_reached 标志if (ic->pb)ic->pb->eof_reached = 0; // FIXME hack, ffplay maybe should not use avio_feof() to test for the end//检查 ic 的输入上下文是否存在,如果存在,则将 eof_reached 标志设为 0。这个标志用于指示输入流是否已经读取到文件末尾,通过将其设为 0,可以重置该标志,以便后续重新读取文件时能够正确处理文件末尾的情况。// 根据文件格式标志设置 seek_by_bytes 选项if (seek_by_bytes < 0)seek_by_bytes = !!(ic->iformat->flags & AVFMT_TS_DISCONT) && strcmp("ogg", ic->iformat->name);// 设置最大帧持续时间is->max_frame_duration = (ic->iformat->flags & AVFMT_TS_DISCONT) ? 10.0 : 3600.0;// 设置窗口标题if (!window_title && (t = av_dict_get(ic->metadata, "title", NULL, 0)))window_title = av_asprintf("%s - %s", t->value, input_filename);// 如果请求了定位,则执行它if (start_time != AV_NOPTS_VALUE) {int64_t timestamp;timestamp = start_time;// 添加流的开始时间if (ic->start_time != AV_NOPTS_VALUE)timestamp += ic->start_time;ret = avformat_seek_file(ic, -1, INT64_MIN, timestamp, INT64_MAX, 0);if (ret < 0) {av_log(NULL, AV_LOG_WARNING, "%s: could not seek to position %0.3f\n",is->filename, (double)timestamp / AV_TIME_BASE);}}// 判断是否为实时流is->realtime = is_realtime(ic);// 如果需要显示状态,则打印流信息if (show_status)av_dump_format(ic, 0, is->filename, 0);// 设置流丢弃策略,并选择最佳流for (i = 0; i < ic->nb_streams; i++) {AVStream* st = ic->streams[i];enum AVMediaType type = st->codecpar->codec_type;st->discard = AVDISCARD_ALL;if (type >= 0 && wanted_stream_spec[type] && st_index[type] == -1)if (avformat_match_stream_specifier(ic, st, wanted_stream_spec[type]) > 0)st_index[type] = i;}/*这段代码的目的是为了选择最佳的音频、视频和字幕流,以备后续使用。通过设置丢弃策略为 AVDISCARD_ALL,可以确保不会使用到不需要的流数据。最终,st_index 数组中记录了每种类型流的最佳索引,以便后续使用。*/for (i = 0; i < AVMEDIA_TYPE_NB; i++) {if (wanted_stream_spec[i] && st_index[i] == -1) {av_log(NULL, AV_LOG_ERROR, "Stream specifier %s does not match any %s stream\n", wanted_stream_spec[i], av_get_media_type_string(i));st_index[i] = INT_MAX;}}// 查找最佳流if (!video_disable)st_index[AVMEDIA_TYPE_VIDEO] =av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO,st_index[AVMEDIA_TYPE_VIDEO], -1, NULL, 0);if (!audio_disable)st_index[AVMEDIA_TYPE_AUDIO] =av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO,st_index[AVMEDIA_TYPE_AUDIO],st_index[AVMEDIA_TYPE_VIDEO],NULL, 0);if (!video_disable && !subtitle_disable)st_index[AVMEDIA_TYPE_SUBTITLE] =av_find_best_stream(ic, AVMEDIA_TYPE_SUBTITLE,st_index[AVMEDIA_TYPE_SUBTITLE],(st_index[AVMEDIA_TYPE_AUDIO] >= 0 ?st_index[AVMEDIA_TYPE_AUDIO] :st_index[AVMEDIA_TYPE_VIDEO]),NULL, 0);//这里的逻辑是,如果有音频流,则使用音频流的索引作为参考流,否则使用视频流的索引作为参考流。这样做是为了尽可能地选择与音频或视频流相关联的字幕流。// 设置显示模式is->show_mode = show_mode;if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) {AVStream* st = ic->streams[st_index[AVMEDIA_TYPE_VIDEO]];AVCodecParameters* codecpar = st->codecpar;AVRational sar = av_guess_sample_aspect_ratio(ic, st, NULL);//根据视频流的信息猜测采样宽高比。if (codecpar->width)set_default_window_size(codecpar->width, codecpar->height, sar);//函数设置默认窗口大小,该函数用于设置视频显示窗口的宽度和高度,以及采样宽高比,以确保视频显示正确比例。}// 打开音频流if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) {stream_component_open(is, st_index[AVMEDIA_TYPE_AUDIO]);}// 尝试打开视频流ret = -1;if (st_index[AVMEDIA_TYPE_VIDEO] >= 0) {ret = stream_component_open(is, st_index[AVMEDIA_TYPE_VIDEO]);}if (is->show_mode == SHOW_MODE_NONE)is->show_mode = ret >= 0 ? SHOW_MODE_VIDEO : SHOW_MODE_RDFT;// 打开字幕流if (st_index[AVMEDIA_TYPE_SUBTITLE] >= 0) {stream_component_open(is, st_index[AVMEDIA_TYPE_SUBTITLE]);}// 如果音频和视频流都未打开,则记录错误并返回if (is->video_stream < 0 && is->audio_stream < 0) {av_log(NULL, AV_LOG_FATAL, "Failed to open file '%s' or configure filtergraph\n",is->filename);ret = -1;goto fail;}// 如果是实时流且未设置 infinite_buffer,则将其设为 1if (is->realtime)infinite_buffer = 1;// 读取数据包for (;;) {// 检查是否需要退出if (is->abort_request)break;// 如果暂停请求已发出且未显示 EOF,则等待暂停if (is->paused != is->last_paused) {is->last_paused = is->paused;if (is->paused)is->read_pause_return = av_read_pause(ic);elseav_read_play(ic);}#if CONFIG_RTSP_DEMUXER || CONFIG_MMSH_PROTOCOLif (is->paused &&(!strcmp(ic->iformat->name, "rtsp") ||(ic->pb && !strncmp(input_filename, "mmsh:", 5)))) {/* wait 10 ms to avoid trying to get another packet *//* XXX: horrible */SDL_Delay(10);continue;}
#endif// 如果发生 EOF 或流结束且队列中还有数据,则等待数据消费if (is->eof) {if (is->videoq.size + is->audioq.size + is->subtitleq.size == 0) {if (loop != 1 && (!loop || --loop)) {stream_seek(is, start_time != AV_NOPTS_VALUE ? start_time : 0, 0, 0);}else if (autoexit) {ret = AVERROR_EOF;goto fail;}}if (is->eof)SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);is->eof = 0;}// 如果 seek 请求已发出,执行 seek 操作if (is->seek_req) {int64_t seek_target = is->seek_pos;int64_t seek_min = is->seek_rel > 0 ? seek_target - is->seek_rel + 2 : INT64_MIN;int64_t seek_max = is->seek_rel < 0 ? seek_target - is->seek_rel - 2 : INT64_MAX;// 记录当前播放位置ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, is->seek_flags);if (ret < 0) {av_log(NULL, AV_LOG_ERROR,"%s: error while seeking\n", is->ic->url);}else {// 清空队列并重置解码器if (is->audio_stream >= 0) {packet_queue_flush(&is->audioq);packet_queue_put(&is->audioq, &flush_pkt);}if (is->subtitle_stream >= 0) {packet_queue_flush(&is->subtitleq);packet_queue_put(&is->subtitleq, &flush_pkt);}if (is->video_stream >= 0) {packet_queue_flush(&is->videoq);packet_queue_put(&is->videoq, &flush_pkt);}if (is->seek_flags & AVSEEK_FLAG_BYTE) {set_clock(&is->extclk, NAN, 0);}else {set_clock(&is->extclk, seek_target / (double)AV_TIME_BASE, 0);}}is->seek_req = 0;is->queue_attachments_req = 1;is->eof = 0;// 重置暂停状态if (is->paused)step_to_next_frame(is);}// 如果队列已满且流未结束,则等待数据消费if (queue_full(&is->videoq) && queue_full(&is->audioq) && queue_full(&is->subtitleq)) {/* wait 10 ms to avoid trying to get another packet */SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);continue;}// 如果队列已满且 stream_end 则等待数据消费if (is->audioq.size + is->videoq.size + is->subtitleq.size >= MAX_QUEUE_SIZE ||(stream_has_enough_packets(is->audio_st, is->audio_stream, &is->audioq) &&stream_has_enough_packets(is->video_st, is->video_stream, &is->videoq) &&stream_has_enough_packets(is->subtitle_st, is->subtitle_stream, &is->subtitleq))) {/* wait 10 ms to avoid trying to get another packet */SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);continue;}// 从输入文件读取数据包ret = av_read_frame(ic, pkt);if (ret < 0) {if ((ret == AVERROR_EOF || avio_feof(ic->pb)) && !is->eof) {if (is->video_stream >= 0)packet_queue_put_nullpacket(&is->videoq, is->video_stream);if (is->audio_stream >= 0)packet_queue_put_nullpacket(&is->audioq, is->audio_stream);if (is->subtitle_stream >= 0)packet_queue_put_nullpacket(&is->subtitleq, is->subtitle_stream);is->eof = 1;}if (ic->pb && ic->pb->error)break;/* wait 10 ms to avoid trying to get another packet */SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);continue;}else {is->eof = 0;}// 如果是附件包且请求队列附件,则入队if (pkt->stream_index == is->audio_stream ||pkt->stream_index == is->video_stream ||pkt->stream_index == is->subtitle_stream) {if (av_packet_make_refcounted(pkt) < 0)continue;packet_queue_put(&is->audioq, pkt);}else {av_packet_unref(pkt);}}ret = 0;
fail:if (ic && !is->ic)avformat_close_input(&ic);if (ret != 0) {SDL_Event event;event.type = FF_QUIT_EVENT;event.user.data1 = is;SDL_PushEvent(&event);}SDL_DestroyMutex(wait_mutex);return 0;
}

stream_component_open 讲解

/* 开启指定流(音频、视频或字幕)。返回0表示成功,否则返回错误码 */
static int stream_component_open(VideoState* is, int stream_index)
{AVFormatContext* ic = is->ic; // 输入上下文指针AVCodecContext* avctx; // 编解码器上下文AVCodec* codec; // 编解码器const char* forced_codec_name = NULL; // 强制使用的编解码器名字AVDictionary* opts = NULL; // 选项字典AVDictionaryEntry* t = NULL; // 用于遍历字典条目的指针int sample_rate, nb_channels; // 采样率和通道数int64_t channel_layout; // 通道布局int ret = 0; // 返回值int stream_lowres = lowres; // 低分辨率设置// 检查流索引有效性if (stream_index < 0 || stream_index >= ic->nb_streams)return -1;// 分配并初始化编解码器上下文avctx = avcodec_alloc_context3(NULL);if (!avctx)return AVERROR(ENOMEM);// 将流的编解码参数复制到编解码器上下文中ret = avcodec_parameters_to_context(avctx, ic->streams[stream_index]->codecpar);if (ret < 0)goto fail;avctx->pkt_timebase = ic->streams[stream_index]->time_base; // 设置时间基// 查找解码器codec = avcodec_find_decoder(avctx->codec_id);// 根据流类型选择强制解码器switch (avctx->codec_type) {case AVMEDIA_TYPE_AUDIO: is->last_audio_stream = stream_index; forced_codec_name = audio_codec_name; break;case AVMEDIA_TYPE_SUBTITLE: is->last_subtitle_stream = stream_index; forced_codec_name = subtitle_codec_name; break;case AVMEDIA_TYPE_VIDEO: is->last_video_stream = stream_index; forced_codec_name = video_codec_name; break;}// 如果设置了强制解码器名,则使用该解码器if (forced_codec_name)codec = avcodec_find_decoder_by_name(forced_codec_name);if (!codec) {// 找不到解码器,打印错误并返回if (forced_codec_name)av_log(NULL, AV_LOG_WARNING, "找不到指定名字的解码器: '%s'\n", forced_codec_name);elseav_log(NULL, AV_LOG_WARNING, "找不到解码器: %s\n", avcodec_get_name(avctx->codec_id));ret = AVERROR(EINVAL);goto fail;}// 设置编解码器IDavctx->codec_id = codec->id;// 限制低分辨率设置不超过解码器支持的最大值if (stream_lowres > codec->max_lowres) {av_log(avctx, AV_LOG_WARNING, "解码器支持的最大低分辨率是 %d\n", codec->max_lowres);stream_lowres = codec->max_lowres;}avctx->lowres = stream_lowres;// 快速解码标志if (fast)avctx->flags2 |= AV_CODEC_FLAG2_FAST;// 应用编解码器选项opts = filter_codec_opts(codec_opts, avctx->codec_id, ic, ic->streams[stream_index], codec);// 添加线程选项(如果没有设置则自动)if (!av_dict_get(opts, "threads", NULL, 0))av_dict_set(&opts, "threads", "auto", 0);// 设置低分辨率选项if (stream_lowres)av_dict_set_int(&opts, "lowres", stream_lowres, 0);// 参考帧计数选项if (avctx->codec_type == AVMEDIA_TYPE_VIDEO || avctx->codec_type == AVMEDIA_TYPE_AUDIO)av_dict_set(&opts, "refcounted_frames", "1", 0);// 打开解码器if ((ret = avcodec_open2(avctx, codec, &opts)) < 0) {goto fail;}// 检查是否有未使用的选项if ((t = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX))) {av_log(NULL, AV_LOG_ERROR, "未找到选项 %s\n", t->key);ret = AVERROR_OPTION_NOT_FOUND;goto fail;}// 重置EOF标志is->eof = 0;// 设置默认丢弃模式ic->streams[stream_index]->discard = AVDISCARD_DEFAULT;switch (avctx->codec_type) {case AVMEDIA_TYPE_AUDIO:
#if CONFIG_AVFILTER{AVFilterContext* sink;// 配置音频过滤源参数,根据编解码器上下文设置采样率、通道数、通道布局和样本格式is->audio_filter_src.freq = avctx->sample_rate;is->audio_filter_src.channels = avctx->channels;is->audio_filter_src.channel_layout = get_valid_channel_layout(avctx->channel_layout, avctx->channels);is->audio_filter_src.fmt = avctx->sample_fmt;// 配置音频过滤器,若失败则跳转到错误处理if ((ret = configure_audio_filters(is, afilters, 0)) < 0)goto fail;// 获取过滤后的sink(输出)参数:采样率、通道数、通道布局sink = is->out_audio_filter;sample_rate = av_buffersink_get_sample_rate(sink);nb_channels = av_buffersink_get_channels(sink);channel_layout = av_buffersink_get_channel_layout(sink);}
#else// 如果没有配置音频过滤器支持,直接使用编解码器的原始参数sample_rate = avctx->sample_rate;nb_channels = avctx->channels;channel_layout = avctx->channel_layout;
#endif// 准备音频输出:根据获取的参数初始化音频输出目标格式,失败则跳转到错误处理if ((ret = audio_open(is, channel_layout, nb_channels, sample_rate, &is->audio_tgt)) < 0)goto fail;// 设置硬件音频缓冲区大小、源缓冲区指针、缓冲区大小和缓冲区索引初始值is->audio_hw_buf_size = ret;is->audio_src = is->audio_tgt;is->audio_buf_size = 0;is->audio_buf_index = 0;// 初始化音频差异平均滤波器系数和计数器,用于音频同步调整is->audio_diff_avg_coef = exp(log(0.01) / AUDIO_DIFF_AVG_NB);is->audio_diff_avg_count = 0;// 定义音频同步修正阈值,基于硬件缓冲区大小和目标格式的时间基is->audio_diff_threshold = (double)(is->audio_hw_buf_size) / is->audio_tgt.bytes_per_sec;// 设置音频流索引和流对象is->audio_stream = stream_index;is->audio_st = ic->streams[stream_index];// 初始化音频解码器上下文,绑定音频队列,并指定读取线程的继续函数decoder_init(&is->auddec, avctx, &is->audioq, is->continue_read_thread);// 如果输入格式不允许搜索且不支持随机访问,则设置解码器的起始PTSif ((is->ic->iformat->flags & (AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK)) && !is->ic->iformat->read_seek) {is->auddec.start_pts = is->audio_st->start_time;is->auddec.start_pts_tb = is->audio_st->time_base;}// 启动音频解码线程,若失败则跳转到错误处理if ((ret = decoder_start(&is->auddec, audio_thread, "audio_decoder", is)) < 0)goto out;// 恢复音频设备播放SDL_PauseAudioDevice(audio_dev, 0);break;case AVMEDIA_TYPE_VIDEO:// 视频流处理:设置视频流索引和流对象,初始化视频解码器上下文并启动解码线程is->video_stream = stream_index;is->video_st = ic->streams[stream_index];decoder_init(&is->viddec, avctx, &is->videoq, is->continue_read_thread);if ((ret = decoder_start(&is->viddec, video_thread, "video_decoder", is)) < 0)goto out;// 标记需要队列附件请求is->queue_attachments_req = 1;break;case AVMEDIA_TYPE_SUBTITLE:// 字幕流处理:设置字幕流索引和流对象,初始化字幕解码器上下文并启动解码线程is->subtitle_stream = stream_index;is->subtitle_st = ic->streams[stream_index];decoder_init(&is->subdec, avctx, &is->subtitleq, is->continue_read_thread);if ((ret = decoder_start(&is->subdec, subtitle_thread, "subtitle_decoder", is)) < 0)goto out;break;default:// 不支持的流类型,直接跳过break;}// 错误处理和清理
fail:avcodec_free_context(&avctx);av_dict_free(&opts);return ret; // 返回结果
}
static int decoder_decode_frame(Decoder* d, AVFrame* frame, AVSubtitle* sub) {int ret = AVERROR(EAGAIN); // 初始化返回值为EAGAIN,表示没有可用帧或需要再次尝试for (;;) { // 无限循环直到解码成功或遇到错误AVPacket pkt; // AVPacket 结构体用于存储视频/音频数据包// 检查队列序列号是否与当前解码器序列号匹配if (d->queue->serial == d->pkt_serial) {do {// 检查是否请求了中止解码if (d->queue->abort_request)return -1;// 根据编解码器类型处理视频或音频帧switch (d->avctx->codec_type) {case AVMEDIA_TYPE_VIDEO:// 尝试从解码器接收视频帧ret = avcodec_receive_frame(d->avctx, frame);if (ret >= 0) { // 成功解码// 根据配置调整帧的PTS(显示时间戳)if (decoder_reorder_pts == -1) frame->pts = frame->best_effort_timestamp;else if (!decoder_reorder_pts) frame->pts = frame->pkt_dts;}break;case AVMEDIA_TYPE_AUDIO:// 尝试从解码器接收音频帧ret = avcodec_receive_frame(d->avctx, frame);if (ret >= 0) {// 调整音频帧的时间戳到正确的时基AVRational tb = (AVRational){1, frame->sample_rate};if (frame->pts != AV_NOPTS_VALUE)frame->pts = av_rescale_q(frame->pts, d->avctx->pkt_timebase, tb);else if (d->next_pts != AV_NOPTS_VALUE)frame->pts = av_rescale_q(d->next_pts, d->next_pts_tb, tb);if (frame->pts != AV_NOPTS_VALUE) {// 更新下一帧的PTS和时间基d->next_pts = frame->pts + frame->nb_samples;d->next_pts_tb = tb;}}}break;}// 处理解码器返回的状态if (ret == AVERROR_EOF) { // 遇到文件结束d->finished = d->pkt_serial;avcodec_flush_buffers(d->avctx); // 清理解码器缓存return 0; // 正常结束}if (ret >= 0) // 成功解码一帧return 1;} while (ret != AVERROR(EAGAIN)); // 若解码器还需要更多数据则继续}// 当前队列无匹配的序列号或需要新数据包时do {// 队列空时通知其他线程可以填充数据if (d->queue->nb_packets == 0)SDL_CondSignal(d->empty_queue_cond);// 使用待处理的数据包if (d->packet_pending) {av_packet_move_ref(&pkt, &d->pkt);d->packet_pending = 0;} else {// 从队列获取新的数据包if (packet_queue_get(d->queue, &pkt, 1, &d->pkt_serial) < 0)return -1;}} while (d->queue->serial != d->pkt_serial); // 确保序列号匹配// 处理数据包if (pkt.data == flush_pkt.data) { // 刷新包avcodec_flush_buffers(d->avctx); // 清理解码器状态d->finished = 0; // 重置结束标记d->next_pts = d->start_pts; // 重置PTSd->next_pts_tb = d->start_pts_tb;} else {// 字幕处理if (d->avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {int got_frame = 0;ret = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, &pkt);if (ret < 0) ret = AVERROR(EAGAIN);else {if (got_frame && !pkt.data) {d->packet_pending = 1;av_packet_move_ref(&d->pkt, &pkt);}ret = got_frame ? 0 : (pkt.data ? AVERROR(EAGAIN) : AVERROR_EOF);}// 非字幕的常规处理} else {if (avcodec_send_packet(d->avctx, &pkt) == AVERROR(EAGAIN)) {av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");d->packet_pending = 1;av_packet_move_ref(&d->pkt, &pkt);}}}av_packet_unref(&pkt); // 释放pkt引用}}
}
http://www.khdw.cn/news/7004.html

相关文章:

  • 公司刚做网站在那里找图片做阿里域名注册官网
  • 郑州品牌营销网站建设微信公众号软文怎么写
  • 网站建设公司没有业务广西seo公司
  • 全国建设建管中心网站外贸网站建站
  • 网站建设服务费如何做会计分录信息流投放平台
  • DZ做的网站百度发布信息的免费平台
  • 沙河口网站建设百度云引擎搜索
  • 广告业网站开发今日热搜排行第一名
  • 肇庆市场核酸检测怎么给网站做优化
  • 高端网站建设成都深圳网络推广团队
  • 婚纱摄影网站设计高端网站建设公司哪家好
  • 企业站群cms武汉seo首页优化报价
  • 免费做网站怎么做网站吗2常用的网络营销方法及效果
  • 广西城市建设学校手机官方网站网站seo分析报告案例
  • 零基础做网站怎么搭建属于自己的网站
  • 做调查问卷网站24小时网站建设
  • 网站建设服类开票税点网站设计公司模板
  • 新网站上线怎么做seo百度关键词优化首选667seo
  • 个人网站开发协议重庆今日头条新闻消息
  • 深圳企业网站制作公司营销案例网站
  • 武汉北京网站建设站长之家whois查询
  • 怎么做网站平台教程怎么网络推广
  • 网站模板 音乐ai智能搜索引擎
  • 政务版企业微信app下载安装安庆seo
  • 增城微网站建设长沙排名优化公司
  • 真人性做爰免费网站苏州百度推广排名优化
  • 空气过滤棉上海网站建设企业文化内容范本
  • 算命先生的网站怎么做临沂森拓网络科技有限公司
  • 洛阳网站的优化百度百科怎么创建自己
  • 做一个模板网站多少钱汕头网站建设方案开发