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

[ffmpeg系列 03] 文件、流地址(视频)解码为YUV

一  代码

ffmpeg版本5.1.2,dll是:ffmpeg-5.1.2-full_build-shared。x64的。

文件、流地址对使用者来说是一样。
流地址(RTMP、HTTP-FLV、RTSP等):信令完成后,才进行音视频传输。信令包括音视频格式、参数等协商。

接流的在实际中的应用:1 展示,播放。2 给算法用,一般是需要RGB格式的。

#ifndef _DECODE_H264_H_
#define _DECODE_H264_H_
#include <string>extern  "C"
{
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavutil/avutil.h"
#include "libavutil/mathematics.h"
#include "libavutil/time.h"
#include "libavutil/pixdesc.h"
#include "libavutil/display.h"
};#pragma  comment(lib, "avformat.lib")
#pragma  comment(lib, "avutil.lib")
#pragma  comment(lib, "avcodec.lib")
#pragma  comment(lib, "swscale.lib")class CDecodeH264
{
public:CDecodeH264();~CDecodeH264();public:public:int DecodeH264();int  Start();int  Close();int  DecodeH264File_Init();int  ReleaseDecode();void H264Decode_Thread_Fun();std::string  dup_wchar_to_utf8(const wchar_t* wstr);double get_rotation(AVStream *st);public:AVFormatContext*       m_pInputFormatCtx = nullptr;AVCodecContext*        m_pVideoDecodeCodecCtx = nullptr;const AVCodec*           m_pCodec = nullptr;	SwsContext*                m_pSwsContext = nullptr;AVFrame*                    m_pFrameScale = nullptr;AVFrame*                    m_pFrameYUV = nullptr;AVPacket*                   m_pAVPacket = nullptr;enum AVMediaType		m_CodecType;int                                    m_output_pix_fmt;int                                    m_nVideoStream = -1;int                                    m_nFrameHeight = 0;int                                    m_nFrameWidth = 0;int                                    m_nFPS;int                                    m_nVideoSeconds;FILE*         m_pfOutYUV = nullptr;FILE*         m_pfOutYUV2 = nullptr;
};
#endif
#include "DecodeH264.h"
#include <thread>
#include <functional>
#include <codecvt>
#include <locale>char av_error2[AV_ERROR_MAX_STRING_SIZE] = { 0 };
#define av_err2str2(errnum) av_make_error_string(av_error2, AV_ERROR_MAX_STRING_SIZE, errnum)CDecodeH264::CDecodeH264()
{	
}CDecodeH264::~CDecodeH264()
{ReleaseDecode();
}std::string  CDecodeH264::dup_wchar_to_utf8(const wchar_t* wstr)
{std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;return converter.to_bytes(wstr);
}//Side data : 
//displaymatrix: rotation of - 90.00 degrees
double CDecodeH264::get_rotation(AVStream *st)
{uint8_t* displaymatrix = av_stream_get_side_data(st,AV_PKT_DATA_DISPLAYMATRIX, NULL);double theta = 0;if (displaymatrix)theta = -av_display_rotation_get((int32_t*)displaymatrix);theta -= 360 * floor(theta / 360 + 0.9 / 360);if (fabs(theta - 90 * round(theta / 90)) > 2)av_log(NULL, AV_LOG_WARNING, "Odd rotation angle.\n""If you want to help, upload a sample ""of this file to https://streams.videolan.org/upload/ ""and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)");return theta;
}int CDecodeH264::DecodeH264File_Init()
{avformat_network_init(); //流地址需要m_pInputFormatCtx = avformat_alloc_context();std::string strFilename = dup_wchar_to_utf8(L"测试.h264");//std::string strFilename = dup_wchar_to_utf8(L"rtmp://127.0.0.1/live/now");	int ret = avformat_open_input(&m_pInputFormatCtx, strFilename.c_str(), nullptr, nullptr);if (ret != 0) {char* err_str = av_err2str2(ret);printf("fail to open filename: %s, return value: %d,  %s\n", strFilename.c_str(), ret, err_str);return -1;}ret = avformat_find_stream_info(m_pInputFormatCtx, nullptr);if (ret < 0) {char* err_str = av_err2str2(ret);printf("fail to get stream information: %d, %s\n", ret, err_str);		return -1;}for (int i = 0; i < m_pInputFormatCtx->nb_streams; ++i) {const AVStream* stream = m_pInputFormatCtx->streams[i];if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {m_nVideoStream = i;printf("type of the encoded data: %d, dimensions of the video frame in pixels: width: %d, height: %d, pixel format: %d\n",stream->codecpar->codec_id, stream->codecpar->width, stream->codecpar->height, stream->codecpar->format);}}if (m_nVideoStream == -1) {printf("no video stream\n");return -1;}printf("m_nVideoStream=%d\n", m_nVideoStream);//获取旋转角度double theta = get_rotation(m_pInputFormatCtx->streams[m_nVideoStream]);m_pVideoDecodeCodecCtx = avcodec_alloc_context3(nullptr);avcodec_parameters_to_context(m_pVideoDecodeCodecCtx,\m_pInputFormatCtx->streams[m_nVideoStream]->codecpar);m_pCodec = avcodec_find_decoder(m_pVideoDecodeCodecCtx->codec_id);if (m_pCodec == nullptr){return -1;}m_nFrameHeight = m_pVideoDecodeCodecCtx->height;m_nFrameWidth = m_pVideoDecodeCodecCtx->width;printf("w=%d h=%d\n", m_pVideoDecodeCodecCtx->width, m_pVideoDecodeCodecCtx->height);if (avcodec_open2(m_pVideoDecodeCodecCtx, m_pCodec, nullptr) < 0){return -1;}//读文件知道视频宽高m_output_pix_fmt = AV_PIX_FMT_YUV420P; //AV_PIX_FMT_NV12;m_pSwsContext = sws_getContext(m_pVideoDecodeCodecCtx->width, m_pVideoDecodeCodecCtx->height,m_pVideoDecodeCodecCtx->pix_fmt, m_pVideoDecodeCodecCtx->width, m_pVideoDecodeCodecCtx->height,(AVPixelFormat)m_output_pix_fmt, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);//解码后的视频数据m_pFrameScale = av_frame_alloc();m_pFrameScale->format = m_output_pix_fmt;m_pFrameYUV = av_frame_alloc();m_pFrameYUV->format = m_output_pix_fmt; //mAVFrame.format is not setm_pFrameYUV->width = m_pVideoDecodeCodecCtx->width;m_pFrameYUV->height = m_pVideoDecodeCodecCtx->height;printf("m_pFrameYUV pix_fmt=%d\n", m_pVideoDecodeCodecCtx->pix_fmt);av_frame_get_buffer(m_pFrameYUV, 64);char cYUVName[256];sprintf_s(cYUVName, "%d_%d_%s.yuv", m_nFrameWidth, m_nFrameHeight, av_get_pix_fmt_name(m_pVideoDecodeCodecCtx->pix_fmt));fopen_s(&m_pfOutYUV, cYUVName, "wb");char cYUVName2[256];sprintf_s(cYUVName2, "%d_%d_%s_2.yuv", m_nFrameWidth, m_nFrameHeight, av_get_pix_fmt_name(m_pVideoDecodeCodecCtx->pix_fmt));fopen_s(&m_pfOutYUV2, cYUVName2, "wb");printf("leave init\n");return   0;
}void CDecodeH264::H264Decode_Thread_Fun()
{int   nFrameFinished = 0;int i = 0;int  ret;m_pAVPacket = av_packet_alloc();while (true) {ret = av_read_frame(m_pInputFormatCtx, m_pAVPacket);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {av_packet_unref(m_pAVPacket);printf("read_frame break");break;}if (m_pAVPacket->stream_index == m_nVideoStream){int  send_packet_ret = avcodec_send_packet(m_pVideoDecodeCodecCtx, m_pAVPacket);printf("encode video send_packet_ret %d\n", send_packet_ret);int receive_frame_ret = avcodec_receive_frame(m_pVideoDecodeCodecCtx, m_pFrameScale);char* err_str = av_err2str2(receive_frame_ret);printf("frame w=%d, h=%d, linesize[0]=%d, linesize[1]=%d\n", m_pFrameScale->width, m_pFrameScale->height, m_pFrameScale->linesize[0], m_pFrameScale->linesize[1]);if (receive_frame_ret == 0){++i;int iReturn = sws_scale(m_pSwsContext, m_pFrameScale->data,m_pFrameScale->linesize, 0, m_nFrameHeight,m_pFrameYUV->data, m_pFrameYUV->linesize);printf("frame w=%d, h=%d, linesize[0]=%d, linesize[1]=%d\n", m_pFrameYUV->width, m_pFrameYUV->height, m_pFrameYUV->linesize[0], m_pFrameYUV->linesize[1]);/*if (0 != iReturn){fwrite(m_pFrameYUV->data[0], 1, m_nFrameWidth * m_nFrameHeight, m_pfOutYUV);fwrite(m_pFrameYUV->data[1], 1, m_nFrameWidth * m_nFrameHeight /4, m_pfOutYUV);fwrite(m_pFrameYUV->data[2], 1, m_nFrameWidth * m_nFrameHeight /4, m_pfOutYUV);}*///用linesize更能兼容特殊的宽if (0 != iReturn){					for (int i = 0; i < m_nFrameHeight; ++i) {fwrite(m_pFrameYUV->data[0] + i * m_pFrameYUV->linesize[0], 1, m_nFrameWidth, m_pfOutYUV2);}for (int i = 0; i < m_nFrameHeight / 2; ++i) {fwrite(m_pFrameYUV->data[1] + i * m_pFrameYUV->linesize[1], 1, m_nFrameWidth / 2, m_pfOutYUV2);}for (int i = 0; i < m_nFrameHeight / 2; ++i) {fwrite(m_pFrameYUV->data[2] + i * m_pFrameYUV->linesize[2], 1, m_nFrameWidth / 2, m_pfOutYUV2);}}				}}av_packet_unref(m_pAVPacket);}
}int CDecodeH264::DecodeH264()
{if (DecodeH264File_Init() != 0){return   -1;}auto video_func = std::bind(&CDecodeH264::H264Decode_Thread_Fun, this);std::thread  video_thread(video_func);video_thread.join(); return 0;
}int CDecodeH264::Start()
{DecodeH264();return  1;
}int CDecodeH264::Close()
{return   0;
}int  CDecodeH264::ReleaseDecode()
{if (m_pSwsContext){sws_freeContext(m_pSwsContext);m_pSwsContext = nullptr;}if (m_pFrameScale){av_frame_free(&m_pFrameScale);//av_frame_alloc()对应}if (m_pFrameYUV){av_frame_free(&m_pFrameYUV);}avcodec_close(m_pVideoDecodeCodecCtx);avformat_close_input(&m_pInputFormatCtx);return 0;}
#include <iostream>
#include <Windows.h>#include "1__DecodeH264/DecodeH264.h"int main()
{CDecodeH264* m_pDecodeVideo = new CDecodeH264();m_pDecodeVideo->Start();return 0;
}

图是雷神博客的:注册函数废弃了,解码函数变了。

二  相关的结构体,方便记忆


1 AVFrame是未压缩的,解码后的数据。AVPacket是压缩的,解码前的数据。知道了这个,编码的send_frame、receive_packet,解码的send_packet、receive_frame,容易记住了。2 2个Context(上下文):Format(混合文件、流地址)、Codec(单个编码格式,比如H264、AAC,编解码实现)AVFormatContext*  m_pInputFormatCtx;AVCodecContext*   m_pVideoDecodeCodecCtx;m_pInputFormatCtx会用到的函数:avformat_open_input、avformat_find_stream_info、 av_read_frame、avformat_close_input。m_pOutputFormatCtx会用到的函数:avcodec_find_decoder、avcodec_open2、avcodec_send_packet、 avcodec_receive_frame。3 AVCodec结构体
const  AVCodec  ff_h264_decoder = {.name                  = "h264",.long_name             = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),.type                  = AVMEDIA_TYPE_VIDEO,.id                    = AV_CODEC_ID_H264,.priv_data_size        = sizeof(H264Context),.init                  = h264_decode_init,.close                 = h264_decode_end,.decode                = h264_decode_frame,……
}static const AVCodec * const codec_list[] = {
...&ff_h264_decoder,
...
};

三 兼容性问题

1 文件名带中文,需要转换。

2 播放竖屏视频(手机录的那种),获取旋转角度。

截图是ffmpeg做法:获取角度后,使用filter调整。

3 宽比较特殊,不是16,32的整数。(比如544x960,544是32的倍数)。用linesize[i]代替宽。

linesize跟cpu有关,是cpu 16、32的倍数。

其它,待更新。

四  为什么需要sws_scale?转换到统一格式I420

sws_scale作用:1 分辨率缩放、 2 不同YUV、RGB格式转换。

H264有记录编码前的YUV采样格式,chroma_format_idc,在sps里。如果没有这个字段,说明该字段用的默认值1,即yuv 4:2:0。

如果YUV的采样格式是yuv 4:2:0,也不要求缩放,不需要sws_scale。

avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);

frame->format记录了yuv的类型。

ffmpeg默认解码成:编码前的yuv格式。即m_pVideoDecodeCodecCtx->pix_fmt。

int ff_decode_frame_props(AVCodecContext *avctx, AVFrame *frame)
{...frame->format              = avctx->pix_fmt;...
}

五  不同格式的time_base

H264的time_base:1/1200000。

flv:音视频都是1/1000。

mp4:视频1/12800(帧率25,怎么算出来的?),音频:1/48000(1/采样频率)。

相关文章:

[ffmpeg系列 03] 文件、流地址(视频)解码为YUV

一 代码 ffmpeg版本5.1.2&#xff0c;dll是&#xff1a;ffmpeg-5.1.2-full_build-shared。x64的。 文件、流地址对使用者来说是一样。 流地址(RTMP、HTTP-FLV、RTSP等)&#xff1a;信令完成后&#xff0c;才进行音视频传输。信令包括音视频格式、参数等协商。 接流的在实际…...

python算法每日一练:连续子数组的最大和

这是一道关于动态规划的算法题&#xff1a; 题目描述&#xff1a; 给定一个整数数组 nums&#xff0c;请找出该数组中连续子数组的最大和&#xff0c;并返回这个最大和。 示例&#xff1a; 输入&#xff1a;[-2, 1, -3, 4, -1, 2, 1, -5, 4] 输出&#xff1a;6 解释&#xff…...

一个vue3的tree组件

https://download.csdn.net/download/weixin_41012767/88709466...

新手练习项目 4:简易2048游戏的实现(C++)

名人说&#xff1a;莫听穿林打叶声&#xff0c;何妨吟啸且徐行。—— 苏轼《定风波莫听穿林打叶声》 Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#xff09; 目录 一、效果图二、代码&#xff08;带注释&#xff09;三、说明 一、效果图 二、代码&#xff08;带…...

2023年度总结:技术沉淀、持续学习

2023年度总结&#xff1a;技术沉淀、持续学习 一、引言 今年是我毕业的第二个年头&#xff0c;也是完整的一年&#xff0c;到了做年终总结的时候了 这一年谈了女朋友&#xff0c;学习了不少技术&#xff0c;是充实且美好的一年&#xff01; 首先先看年初定的小目标&#xf…...

Unity 利用UGUI之Slider制作进度条

在Unity中使用Slider和Text组件可以制作简单的进度条。 首先在场景中右键->UI->Slider&#xff0c;新建一个Slider组件&#xff1a; 同样方法新建一个Text组件&#xff0c;最终如图&#xff1a; 创建一个进度模拟脚本&#xff0c;Slider_Progressbar.cs using System.C…...

OCS2 入门教程(四)- 机器人示例

系列文章目录 前言 OCS2 包含多个机器人示例。我们在此简要讨论每个示例的主要特点。 System State Dim. Input Dim. Constrained Caching Double Integrator 2 1 No No Cartpole 4 1 Yes No Ballbot 10 3 No No Quadrotor 12 4 No No Mobile Manipul…...

FreeRTOS学习第6篇–任务状态挂起恢复删除等操作

目录 FreeRTOS学习第6篇--任务状态挂起恢复删除等操作任务的状态设计实验IRReceiver_Task任务相关代码片段实验现象本文中使用的测试工程 FreeRTOS学习第6篇–任务状态挂起恢复删除等操作 本文目标&#xff1a;学习与使用FreeRTOS中的几项操作&#xff0c;有挂起恢复删除等操作…...

BLE Mesh蓝牙组网技术详细解析之Access Layer访问层(六)

目录 一、什么是BLE Mesh Access Layer访问层&#xff1f; 二、Access payload 2.1 Opcode 三、Access layer behavior 3.1 Access layer发送消息的流程 3.2 Access layer接收消息的流程 3.3 Unacknowledged and acknowledged messages 3.3.1 Unacknowledged message …...

Netlink 通信机制

文章目录 前言一、Netlink 介绍二、示例代码参考资料 前言 一、Netlink 介绍 Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。 在Linux 内核中&#xff0c;使用netlink 进行应用与内核通信的应用有…...

2024.1.8每日一题

LeetCode 回旋镖的数量 447. 回旋镖的数量 - 力扣&#xff08;LeetCode&#xff09; 题目描述 给定平面上 n 对 互不相同 的点 points &#xff0c;其中 points[i] [xi, yi] 。回旋镖 是由点 (i, j, k) 表示的元组 &#xff0c;其中 i 和 j 之间的距离和 i 和 k 之间的欧式…...

看了致远OA的表单设计后的思考

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; https://gitee.com/nbacheng/n…...

mmdetection训练自己的数据集

mmdetection训练自己的数据集 这里写目录标题 mmdetection训练自己的数据集一&#xff1a; 环境搭建二&#xff1a;数据集格式转换(yolo转coco格式)yolo数据集格式coco数据集格式yolo转coco数据集格式yolo转coco数据集格式的代码 三&#xff1a; 训练dataset数据文件配置config…...

MySQL取出N列里最大or最小的一个数据

如题&#xff0c;现在有3列&#xff0c;都是数字类型&#xff0c;要取出这3列里最大或最小的的一个数字 -- N列取最小 SELECT LEAST(temperature_a,temperature_b,temperature_c) min FROM infrared_heat-- N列取最大 SELECT GREATEST(temperature_a,temperature_b,temperat…...

编写.NET的Dockerfile文件构建镜像

创建一个WebApi项目&#xff0c;并且创建一个Dockerfile空文件&#xff0c;添加以下代码&#xff0c;7.0代表的你项目使用的SDK的版本&#xff0c;构建的时候也需要选择好指定的镜像tag FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443F…...

【C语言】浙大版C语言程序设计(第三版) 练习7-4 找出不是两个数组共有的元素

前言 最近在学习浙大版的《C语言程序设计》&#xff08;第三版&#xff09;教材&#xff0c;同步在PTA平台上做对应的练习题。这道练习题花了比较长的时间&#xff0c;于是就写篇博文记录一下我的算法和代码。 2024.01.03 题目 练习7-4 找出不是两个数组共有的元素 作者 张彤…...

7.27 SpringBoot项目实战 之 整合Swagger

文章目录 前言一、Maven依赖二、编写Swagger配置类三、编写接口配置3.1 控制器Controller 配置描述3.2 接口API 配置描述3.3 参数配置描述3.4 忽略API四、全局参数配置五、启用增强功能六、调试前言 在我们实现了那么多API以后,进入前后端联调阶段,需要给前端同学提供接口文…...

创建第一个SpringMVC项目,入手必看!

文章目录 创建第一个SpringMVC项目&#xff0c;入手必看&#xff01;1、新建一个maven空项目&#xff0c;在pom.xml中设置打包为war之前&#xff0c;右击项目添加web框架2、如果点击右键没有添加框架或者右击进去后没有web框架&#xff0c;点击左上角file然后进入项目结构在模块…...

go 切片长度与容量的区别

切片的声明 切片可以看成是数组的引用&#xff08;实际上切片的底层数据结构确实是数组&#xff09;。在 Go 中&#xff0c;每个数组的大小是固定的&#xff0c;不能随意改变大小&#xff0c;切片可以为数组提供动态增长和缩小的需求&#xff0c;但其本身并不存储任何数据。 …...

回归和分类区别

回归任务&#xff08;Regression&#xff09;&#xff1a; 特点&#xff1a; 输出是连续值&#xff0c;通常是实数。任务目标是预测或估计一个数值。典型应用包括房价预测、销售额预测、温度预测等。 目标&#xff1a; 最小化预测值与真实值之间的差异&#xff0c;通常使用…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

C# 表达式和运算符(求值顺序)

求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如&#xff0c;已知表达式3*52&#xff0c;依照子表达式的求值顺序&#xff0c;有两种可能的结果&#xff0c;如图9-3所示。 如果乘法先执行&#xff0c;结果是17。如果5…...