iOS 视频压缩 mov转mp4 码率

news2024/12/26 0:10:20

最近还是因为IM模块的功能,IOS录制MOV视频发送后,安卓端无法播放,迫不得已兼容将MOV视频转为MP4发送。

其中mov视频包括4K/24FPS、4K/30FPS、4K/60FPS、720p HD/30FPS、1080p HD/30FPS、1080p HD/60FPS!

使用AVAssetExportSession作为导出工具,指定压缩质量AVAssetExportPresetMediumQuality,这样能有效的减少视频体积,但是视频画面清晰度比较差,举个例子:一个25秒的1080p视频,经过压缩后从1080p变为320p,大小从34m变成2.6m。

AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:avAsset presetName:AVAssetExportPresetMediumQuality];

exportSession.outputURL= url;

exportSession.shouldOptimizeForNetworkUse = YES;

exportSession.outputFileType = AVFileTypeMPEG4;

[exportSessionexportAsynchronouslyWithCompletionHandler:^{

            switch([exportSessionstatus]) {

                case AVAssetExportSessionStatusFailed:

                    NSLog(@"Export canceled");

                    break;

                case AVAssetExportSessionStatusCancelled:

                    NSLog(@"Export canceled");

                    break;

                case AVAssetExportSessionStatusCompleted:{

                    NSLog(@"Successful!");

                    break;

                }

                default:break;

            }

重新梳理下我们的需求,我们的场景对视频质量要求稍高,对视频的大小容忍比较高,所以将最大分辨率设为720p。

所以我们的压缩设置改为AVAssetExportPreset1280x720,压缩后大小几乎没变,从34m变成32.5m。我们可以用mideaInfo来查看下两个视频文件到底有什么区别,上图为1080p,下图为720p:

由上图可以看到,两个分辨率差别巨大的视频,大小居然差不多,要分析其中的原因首先要了解H264编码。

3.H264编码

关于H264编码的原理可以参考(这篇文章),本文不详细展开,只说明几个参数。

Bit Rate:

比特率是指每秒传送的比特(bit)数。单位为 bps(Bit Per Second),比特率越高,每秒传送数据就越多,画质就越清晰。声音中的比特率是指将模拟声音信号转换成数字声音信号后,单位时间内的二进制数据量,是间接衡量音频质量的一个指标。 视频中的比特率(码率)原理与声音中的相同,都是指由模拟信号转换为数字信号后,单位时间内的二进制数据量。

所以选择适合的比特率是压缩视频大小的关键,比特率设置太小的话,视频会变得模糊,失真。比特率太高的话,视频数据太大,又达不到我们压缩的要求。

Format profile:

作为行业标准,H.264编码体系定义了4种不同的Profile(类):Baseline(基线类),Main(主要类), Extended(扩展类)和High Profile(高端类)(它们各自下分成许多个层):
Baseline Profile 提供I/P帧,仅支持progressive(逐行扫描)和CAVLC;
Extended Profile 提供I/P/B/SP/SI帧,仅支持progressive(逐行扫描)和CAVLC;
Main Profile 提供I/P/B帧,支持progressive(逐行扫描)和interlaced(隔行扫描),提供CAVLC或CABAC;
High Profile (也就是FRExt)在Main Profile基础上新增:8x8 intra prediction(8x8 帧内预测), custom quant(自定义量化), lossless video coding(无损视频编码), 更多的yuv格式(4:4:4...);

从压缩比例来说 从压缩比例来说,baseline< main < high,由于上图中720p是Main@L3.1,1080p是High@L4,这就是明明分辨率不一样,但是压缩后的大小却差不多的原因。

关于iPhone设备对的支持

  • iPhone 3GS 和更早的设备支持 Baseline Profile level 3.0 及更低的级别

  • iPhone 4S 支持 High Profile level 4.1 及更低的级别

  • iPhone 5C 支持 High Profile level 4.1 及更低的级别

  • iPhone 5S 支持 High Profile level 4.1 及更低的级别

  • iPad 1 支持 Main Profile level 3.1 及更低的级别

  • iPad 2 支持 Main Profile level 3.1 及更低的级别

  • iPad with Retina display 支持 High Profile level 4.1 及更低的级别

  • iPad mini 支持 High Profile level 4.1 及更低的级别

GOP

GOP 指的就是两个I帧之间的间隔。
在视频编码序列中,主要有三种编码帧:I帧、P帧、B帧。

  1. I帧即Intra-coded picture(帧内编码图像帧),不参考其他图像帧,只利用本帧的信息进行编码
  2. P帧即Predictive-codedPicture(预测编码图像帧),利用之前的I帧或P帧,采用运动预测的方式进行帧间预测编码
  3. B帧即Bidirectionallypredicted picture(双向预测编码图像帧),提供最高的压缩比,它既需要之前的图
    像帧(I帧或P帧),也需要后来的图像帧(P帧),采用运动预测的方式进行帧间双向预测编码
      在视频编码序列中,GOP即Group of picture(图像组),指两个I帧之间的距离,Reference(参考周期)指两个P帧之间的距离。一个I帧所占用的字节数大于一个P帧,一个P帧所占用的字节数大于一个B帧。

所以在码率不变的前提下,GOP值越大,P、B帧的数量会越多,平均每个I、P、B帧所占用的字节数就越多,也就更容易获取较好的图像质量;Reference越大,B帧的数量越多,同理也更容易获得较好的图像质量。
  需要说明的是,通过提高GOP值来提高图像质量是有限度的,在遇到场景切换的情况时,H.264编码器会自动强制插入一个I帧,此时实际的GOP值被缩短了。另一方面,在一个GOP中,P、B帧是由I帧预测得到的,当I帧的图像质量比较差时,会影响到一个GOP中后续P、B帧的图像质量,直到下一个GOP开始才有可能得以恢复,所以GOP值也不宜设置过大。
  同时,由于P、B帧的复杂度大于I帧,所以过多的P、B帧会影响编码效率,使编码效率降低。另外,过长的GOP还会影响Seek操作的响应速度,由于P、B帧是由前面的I或P帧预测得到的,所以Seek操作需要直接定位,解码某一个P或B帧时,需要先解码得到本GOP内的I帧及之前的N个预测帧才可以,GOP值越长,需要解码的预测帧就越多,seek响应的时间也越长。
M 和 N :M值表示I帧或者P帧之间的帧数目,N值表示GOP的长度。N的至越大,代表压缩率越大。因为图2中N=15远小于图一中N=30。这也是720p尺寸压缩不理想的原因。

4.解决思路

由上可知压缩视频主要可以采用以下几种手段:

  • 降低分辨率
  • 降低码率
  • 指定高的 Format profile

由于业务指定分辨率为720p,所以我们只能尝试另外两种方法。

降低码率

根据这篇文章Video Encoding Settings for H.264 Excellence,推荐了适合720p的推荐码率为2400~3700之间。之前压缩的文件码率为9979,所以码率还是有很大的优化空间的。

宽屏

非宽屏

指定高的 Format profile

由于现在大部分的设备都支持High Profile level,所以我们可以把Format profileMain Profile level改为High Profile level

现在我们已经知道要做什么了,那么怎么做呢?

5.解决方法

由于之前的AVAssetExportSession不能指定码率和Format profile,我们这里需要使用AVAssetReaderAVAssetWriter

AVAssetReader负责将数据从asset里拿出来,AVAssetWriter负责将得到的数据存成文件。
核心代码如下:

//生成reader 和 writer
    self.reader = [AVAssetReader.alloc initWithAsset:self.asset error:&readerError];

    self.writer = [AVAssetWriter assetWriterWithURL:self.outputURL fileType:self.outputFileType error:&writerError];
//视频
    if (videoTracks.count > 0) {
        self.videoOutput = [AVAssetReaderVideoCompositionOutput assetReaderVideoCompositionOutputWithVideoTracks:videoTracks videoSettings:self.videoInputSettings];
        self.videoOutput.alwaysCopiesSampleData = NO;
        if (self.videoComposition)
        {
            self.videoOutput.videoComposition = self.videoComposition;
        }
        else
        {
            self.videoOutput.videoComposition = [self buildDefaultVideoComposition];
        }
        if ([self.reader canAddOutput:self.videoOutput])
        {
            [self.reader addOutput:self.videoOutput];
        }

        //
        // Video input
        //
        self.videoInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:self.videoSettings];
        self.videoInput.expectsMediaDataInRealTime = NO;
        if ([self.writer canAddInput:self.videoInput])
        {
            [self.writer addInput:self.videoInput];
        }
        NSDictionary *pixelBufferAttributes = @
        {
            (id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
            (id)kCVPixelBufferWidthKey: @(self.videoOutput.videoComposition.renderSize.width),
            (id)kCVPixelBufferHeightKey: @(self.videoOutput.videoComposition.renderSize.height),
            @"IOSurfaceOpenGLESTextureCompatibility": @YES,
            @"IOSurfaceOpenGLESFBOCompatibility": @YES,
        };
        self.videoPixelBufferAdaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:self.videoInput sourcePixelBufferAttributes:pixelBufferAttributes];
    }

//音频
   NSArray *audioTracks = [self.asset tracksWithMediaType:AVMediaTypeAudio];
    if (audioTracks.count > 0) {
      self.audioOutput = [AVAssetReaderAudioMixOutput assetReaderAudioMixOutputWithAudioTracks:audioTracks audioSettings:nil];
      self.audioOutput.alwaysCopiesSampleData = NO;
      self.audioOutput.audioMix = self.audioMix;
      if ([self.reader canAddOutput:self.audioOutput])
      {
          [self.reader addOutput:self.audioOutput];
      }
    } else {
        // Just in case this gets reused
        self.audioOutput = nil;
    }

    //
    // Audio input
    //
    if (self.audioOutput) {
        self.audioInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:self.audioSettings];
        self.audioInput.expectsMediaDataInRealTime = NO;
        if ([self.writer canAddInput:self.audioInput])
        {
            [self.writer addInput:self.audioInput];
        }
    }

//开始读写
    [self.writer startWriting];
    [self.reader startReading];
    [self.writer startSessionAtSourceTime:self.timeRange.start];

//压缩完成的回调

__block BOOL videoCompleted = NO;
    __block BOOL audioCompleted = NO;
    __weak typeof(self) wself = self;
    self.inputQueue = dispatch_queue_create("VideoEncoderInputQueue", DISPATCH_QUEUE_SERIAL);
    if (videoTracks.count > 0) {
        [self.videoInput requestMediaDataWhenReadyOnQueue:self.inputQueue usingBlock:^
        {
            if (![wself encodeReadySamplesFromOutput:wself.videoOutput toInput:wself.videoInput])
            {
                @synchronized(wself)
                {
                    videoCompleted = YES;
                    if (audioCompleted)
                    {
                        [wself finish];
                    }
                }
            }
        }];
    }
    else {
        videoCompleted = YES;
    }
    
    if (!self.audioOutput) {
        audioCompleted = YES;
    } else {
        [self.audioInput requestMediaDataWhenReadyOnQueue:self.inputQueue usingBlock:^
         {
             if (![wself encodeReadySamplesFromOutput:wself.audioOutput toInput:wself.audioInput])
             {
                 @synchronized(wself)
                 {
                     audioCompleted = YES;
                     if (videoCompleted)
                     {
                         [wself finish];
                     }
                 }
             }
         }];
    }

其中self.videoInput里的self.videoSettings我们需要对视频压缩参数做设置

self.videoSettings = @
{
    AVVideoCodecKey: AVVideoCodecH264,
    AVVideoWidthKey: @1280,
    AVVideoHeightKey: @720,
    AVVideoCompressionPropertiesKey: @
    {
        AVVideoAverageBitRateKey: @3000000,
        AVVideoProfileLevelKey: AVVideoProfileLevelH264High40,
    },
};

封装好的控件可以参考https://blog.csdn.net/yunhuaikong/article/details/133300420?spm=1001.2014.3001.5502。

6.最终效果

通过下图我们可以看到,视频已经成功被压缩成10m左右。结合视频效果,这个压缩成果我们还是很满意的。

压缩后的文件

下面是视频部分画面截图的效果:

                                                                原视频,34m

                                                原来的MediumQuality压缩效果,2.6m

                                                         原来的720p压缩效果,32.5m

                                                        优化后的720p的压缩效果,10m

7.视频转码时遇到的坑

使用 SDAVAssetExportSession 时遇到一个坑,大部分视频转码没问题,部分视频转码会有黑屏问题,最后定位出现问题的代码如下:


- (AVMutableVideoComposition *)buildDefaultVideoComposition
{
    AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];
    AVAssetTrack *videoTrack = [[self.asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];

    // get the frame rate from videoSettings, if not set then try to get it from the video track,
    // if not set (mainly when asset is AVComposition) then use the default frame rate of 30
    float trackFrameRate = 0;
    if (self.videoSettings)
    {
        NSDictionary *videoCompressionProperties = [self.videoSettings objectForKey:AVVideoCompressionPropertiesKey];
        if (videoCompressionProperties)
        {
            NSNumber *frameRate = [videoCompressionProperties objectForKey:AVVideoAverageNonDroppableFrameRateKey];
            if (frameRate)
            {
                trackFrameRate = frameRate.floatValue;
            }
        }
    }
    else
    {
        trackFrameRate = [videoTrack nominalFrameRate];
    }

    if (trackFrameRate == 0)
    {
        trackFrameRate = 30;
    }

    videoComposition.frameDuration = CMTimeMake(1, trackFrameRate);
    CGSize targetSize = CGSizeMake([self.videoSettings[AVVideoWidthKey] floatValue], [self.videoSettings[AVVideoHeightKey] floatValue]);
    CGSize naturalSize = [videoTrack naturalSize];
    CGAffineTransform transform = videoTrack.preferredTransform;
    // Workaround radar 31928389, see https://github.com/rs/SDAVAssetExportSession/pull/70 for more info
    if (transform.ty == -560) {
        transform.ty = 0;
    }

    if (transform.tx == -560) {
        transform.tx = 0;
    }

    CGFloat videoAngleInDegree  = atan2(transform.b, transform.a) * 180 / M_PI;
    if (videoAngleInDegree == 90 || videoAngleInDegree == -90) {
        CGFloat width = naturalSize.width;
        naturalSize.width = naturalSize.height;
        naturalSize.height = width;
    }
    videoComposition.renderSize = naturalSize;
    // center inside
    {
        float ratio;
        float xratio = targetSize.width / naturalSize.width;
        float yratio = targetSize.height / naturalSize.height;
        ratio = MIN(xratio, yratio);

        float postWidth = naturalSize.width * ratio;
        float postHeight = naturalSize.height * ratio;
        float transx = (targetSize.width - postWidth) / 2;
        float transy = (targetSize.height - postHeight) / 2;

        CGAffineTransform matrix = CGAffineTransformMakeTranslation(transx / xratio, transy / yratio);
        matrix = CGAffineTransformScale(matrix, ratio / xratio, ratio / yratio);
        transform = CGAffineTransformConcat(transform, matrix);
    }

    // Make a "pass through video track" video composition.
    AVMutableVideoCompositionInstruction *passThroughInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
    passThroughInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, self.asset.duration);

    AVMutableVideoCompositionLayerInstruction *passThroughLayer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];

    [passThroughLayer setTransform:transform atTime:kCMTimeZero];

    passThroughInstruction.layerInstructions = @[passThroughLayer];
    videoComposition.instructions = @[passThroughInstruction];

    return videoComposition;
}
1. transform 不正确引起的黑屏
CGAffineTransform transform = videoTrack.preferredTransform;

1.参考评论区 @baopanpan同学的说法,TZImagePickerController可以解决,找到代码试了一下,确实不会出现黑屏的问题了,代码如下

/// 获取优化后的视频转向信息
- (AVMutableVideoComposition *)fixedCompositionWithAsset:(AVAsset *)videoAsset {
    AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];
    // 视频转向
    int degrees = [self degressFromVideoFileWithAsset:videoAsset];
    if (degrees != 0) {
        CGAffineTransform translateToCenter;
        CGAffineTransform mixedTransform;
        videoComposition.frameDuration = CMTimeMake(1, 30);
        
        NSArray *tracks = [videoAsset tracksWithMediaType:AVMediaTypeVideo];
        AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
        
        AVMutableVideoCompositionInstruction *roateInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
        roateInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, [videoAsset duration]);
        AVMutableVideoCompositionLayerInstruction *roateLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack];
        
        if (degrees == 90) {
            // 顺时针旋转90°
            translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.height, 0.0);
            mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI_2);
            videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.height,videoTrack.naturalSize.width);
            [roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero];
        } else if(degrees == 180){
            // 顺时针旋转180°
            translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.width, videoTrack.naturalSize.height);
            mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI);
            videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.width,videoTrack.naturalSize.height);
            [roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero];
        } else if(degrees == 270){
            // 顺时针旋转270°
            translateToCenter = CGAffineTransformMakeTranslation(0.0, videoTrack.naturalSize.width);
            mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI_2*3.0);
            videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.height,videoTrack.naturalSize.width);
            [roateLayerInstruction setTransform:mixedTransform atTime:kCMTimeZero];
        }else {//增加异常处理
            videoComposition.renderSize = CGSizeMake(videoTrack.naturalSize.width,videoTrack.naturalSize.height);
        }
        
        roateInstruction.layerInstructions = @[roateLayerInstruction];
        // 加入视频方向信息
        videoComposition.instructions = @[roateInstruction];
    }
    return videoComposition;
}

/// 获取视频角度
- (int)degressFromVideoFileWithAsset:(AVAsset *)asset {
    int degress = 0;
    NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
    if([tracks count] > 0) {
        AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
        CGAffineTransform t = videoTrack.preferredTransform;
        if(t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0){
            // Portrait
            degress = 90;
        } else if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0){
            // PortraitUpsideDown
            degress = 270;
        } else if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0){
            // LandscapeRight
            degress = 0;
        } else if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0){
            // LandscapeLeft
            degress = 180;
        }
    }
    return degress;
}
2. naturalSize 不正确的坑

之前用模拟器录屏得到一个视频,调用[videoTrack naturalSize]的时候,得到的size为 (CGSize) naturalSize = (width = 828, height = 0.02734375),明显是不正确的,这个暂时没有找到解决办法,知道的同学可以在下面评论一下。

3. 视频黑边问题

视频黑边应该是视频源尺寸和目标尺寸比例不一致造成的,需要根据原尺寸的比例算出目标尺寸

   
            CGSize targetSize = CGSizeMake(videoAsset.pixelWidth, videoAsset.pixelHeight);
            //尺寸过大才压缩,否则不更改targetSize
            if (targetSize.width * targetSize.height > 1280 * 720) {
                int width = 0,height = 0;
                if (targetSize.width > targetSize.height) {
                    width = 1280;
                    height = 1280 * targetSize.height/targetSize.width;
                }else {
                    width = 720;
                    height = 720 * targetSize.height/targetSize.width;
                }
                targetSize = CGSizeMake(width, height);
            }else if (targetSize.width == 0 || targetSize.height == 0) {//异常情况处理
                targetSize = CGSizeMake(720, 1280);
            }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1045077.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

14. Redisson 分布式锁

Spring Cloud 微服务系列文章&#xff0c;点击上方合集↑ 1. 开头 在单体应用中&#xff0c;我们可以用Java的synchronized或lock来使用锁&#xff0c;但在微服务的场景下&#xff0c;一个应用会部署多个实例&#xff0c;就需要保证多个实例的多个线程同时只能有一个线程来操…

破信息壁垒,亿发一站式ERP系统建设,打造五金制造信息管理平台

五金制造拥有明显的行业特征&#xff0c;如体量小、品种繁多、颜色多样、加工工艺不断演进等&#xff0c;呈现出一种独特的管理挑战。大多数五金企业仍然依赖人工管理和经验决策&#xff0c;如今需要寻求更合理和科学的决策方法&#xff0c;以实现生产、销售、仓储、采购和财务…

无人机如何做到自动巡检?关键技术步骤分析

无人机应用在电网、水利、交通、城管等巡逻巡检领域带来了巡视效率的提升。同时飞手操作的难度和门槛、野外环境的影响、巡检结果处理难度大等带来一系列的巡检问题&#xff0c;自动化的无人机巡检则能很好的解决这些问题&#xff0c;比如我们比较熟知的自动机场&#xff0c;它…

【DETR】

https://tianfeng.space/ 前言 论文 代码 DETR&#xff08;Data-efficient Image Transformer&#xff09;是一种用于目标检测任务的深度学习模型。它与传统的目标检测方法不同&#xff0c;采用了Transformer架构&#xff0c;将目标检测问题转化为一个序列到序列的问题。以下…

【广州华锐互动】VR消防队灭火实训:让消防安全教育变得更生动有趣!

VR消防队灭火实训是一种基于虚拟现实技术的消防培训及模拟&#xff0c;学习如何在火灾中保护自己的自救和逃生方法、技能。这种平台可以让市民在虚拟环境中进行火灾逃生训练&#xff0c;提高人的消防意识和自救能力。 传统的消防培训方式通常是通过理论讲解和现场演示来进行&am…

《论文阅读27》SuperGlue: Learning Feature Matching with Graph Neural Networks

一、论文 研究领域&#xff1a; 图像特征点匹配论文&#xff1a;SuperGlue: Learning Feature Matching with Graph Neural NetworksCVPR 2020veido论文code 二、论文简述 [参考] [参考] [参考] 三、论文详述 SuperGlue&#xff1a;使用图神经网络学习特征匹配 本文介绍了…

【AI视野·今日Sound 声学论文速览 第十一期】Mon, 25 Sep 2023

AI视野今日CS.Sound 声学论文速览 Mon, 25 Sep 2023 Totally 1 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Sound Papers Deepfake audio as a data augmentation technique for training automatic speech to text transcription models Authors Alexandre R. …

Hashable/哈希协议, Arrays/数组 的使用

1. Hashable 模型实现哈希协议 1.1 实现 /// Identifiable struct MyCustomModel: Hashable{//let id UUID().uuidStringlet title: Stringfunc hash(into hasher: inout Hasher) {hasher.combine(title)} }/// 哈希协议: 唯一标识值 struct HashableBootcamp: View {// 每个…

山西电力市场日前价格预测【2023-09-27】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-09-27&#xff09;山西电力市场全天平均日前电价为342.48元/MWh。其中&#xff0c;最高日前电价为454.24元/MWh&#xff0c;预计出现在18: 30。最低日前电价为171.32元/MWh&#xff0c;预计…

2023 年度编程语言榜单排名揭晓,Python稳坐多项第一

随着科技的迅速发展&#xff0c;编程语言已成为现代社会不可或缺的一部分&#xff1a; 无论是网站开发、移动应用、人工智能还是嵌入式系统&#xff0c;编程语言都扮演着关键角色。 当下流行的AI人工智能大多是通过Python语言实现的。 作为AI技术框架的基础语言&#xff0c;…

华为OD机试 - 工号不够用了怎么办 - 双指针(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…

[CSCCTF 2019 Qual]FlaskLight 过滤 url_for globals 绕过globals过滤

目录 subprocess.Popen FILE warnings.catch_warnings site._Printer 这题很明显就是 SSTI了 源代码 我们试试看 {{7*7}} 然后我们就开始吧 原本我的想法是直接{{url_for.__globals__}} 但是回显是直接500 猜测过滤 我们正常来吧 {{"".__class__}} 查看当前…

项目经理如何顺利推进项目:做好任务规划,合理安排时间

在一个项目中&#xff0c;项目经理经常需面对超负荷的工作&#xff0c;并需要通过加班来达成每日的工作任务&#xff0c;他们时常需处理各种任务和大量的电子邮件。这导致项目经理在项目实施过程中&#xff0c;经常处于“救火”状态&#xff0c;而无法有效管理项目进程&#xf…

性能测试分类

一. 基准测试 二. 负载测试 通过逐步增加系统负载&#xff0c;测试系统性能的变化&#xff0c;在满足性能指标的前提下&#xff0c;系统所能承受的最大负载量的测试。 健身&#xff1a;举哑铃 10斤哑铃&#xff0c;举起10个需要15s 20斤哑铃&#xff0c;举起10个需要15s 30斤…

上海亚商投顾:沪指缩量调整 新型工业化板块掀涨停潮

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指昨日缩量震荡调整&#xff0c;创业板指再度跌破2000点关口。新型工业化板块掀涨停潮&#xff0c;华辰装备…

SourceTree 账号或者密码输入错误 Incorrect username or password ( access token )解决办法

修改来修改去一直解决不了&#xff0c;那就试试查看一下源文件记录的账号密码吧&#xff01;

聚势共赢!爱创科技喜获“腾讯健康年度优秀合作伙伴”!

2023年9月7日—8日&#xff0c;2023腾讯全球数字生态大会在深圳国际会展中心成功举办。来自行业的重磅院士学者、企业代表、生态伙伴等共赴大会&#xff0c;围绕“智变加速&#xff0c;产业焕新”这一活动主题进行了深层次、全方位的交流和探讨&#xff0c;共话大模型时代下数智…

华为智能企业上网行为管理安全解决方案(2)

本文承接&#xff1a; https://blog.csdn.net/qq_37633855/article/details/133339254?spm1001.2014.3001.5501 重点讲解华为智能企业上网行为管理安全解决方案的部署流程。 华为智能企业上网行为管理安全解决方案&#xff08;2&#xff09; 课程地址方案部署整体流程组网规划…

【云上探索实验室】编程范式变革进行时——CodeWhisperer实践全流程及测评分析报告

目录 一、基于LLM的辅助编程——编程范式变革进行时二、CodeWhisperer VS Code 安装与配置2.1、扩展安装2.2、配置 三、CodeWhisperer实践全流程3.1、CodeWhisperer基础实验3.2、CodeWhisperer项目实践——Web端宝可梦图鉴 四、CodeWhisperer测评分析报告4.1、功能性分析4.2、…

AI赋能的3D资产管理

推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 想象一下&#xff0c;作为一名视频游戏设计师&#xff0c;你希望在游戏中使用 3D 龙模型。 以前&#xff0c;你可以通过两种方式执行此操作&#xff1a; 自己制作复杂的 3D 模型或从多个角度拍摄龙模型的照片。前往 3D 模…