如何使用ffmpeg生成視頻縮略圖

核心思路

使用ffmpeg獲取視頻的第一幀關鍵幀,轉換成UIImage,然后保存成jpg圖片。如果不需要持久化,直接使用UIImage對象即可

ffmpeg手動集成

我直接使用了ffmpeg-kit進行ffmpeg的打包,打包腳本如下

ffmpeg-kit/tools/release/ios.sh

最后可以在以下目錄找到產(chǎn)物

ffmpeg-kit/prebuilt/bundle-apple-cocoapods-ios/ffmpeg-kit-ios-min/

Podfile指向該目錄下的ffmpeg-kit-ios-min.podspec即可,或者傳到自己的git repo上。

代碼實現(xiàn)

使用ffmpeg打開視頻文件

AVFormatContext *context = avformat_alloc_context();
// 通過文件創(chuàng)建AVFormatContext
int ret;
ret = avformat_open_input(&context, [videoPath UTF8String], NULL, NULL);
if (ret != 0) goto free_res;
// 尋找流信息
ret = avformat_find_stream_info(context, NULL);
if (ret != 0) goto free_res;

尋找視頻流

// 尋找視頻流
AVStream *videoStream = NULL;
int videoStreamIndex = -1;
for (int i = 0; i < context->nb_streams; ++i) {
  if (context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
    videoStream = context->streams[i];
    videoStreamIndex = i;
    break;
  }
}
if (!videoStream) goto free_res;

這里還存了一下視頻流的索引值,方便后續(xù)比對

創(chuàng)建解碼器

// 創(chuàng)建視頻解碼器
const AVCodec *videoCodec = avcodec_find_decoder(videoStream->codecpar->codec_id);
AVCodecContext *videoCodecContext = avcodec_alloc_context3(videoCodec);
avcodec_parameters_to_context(videoCodecContext, videoStream->codecpar);
ret = avcodec_open2(videoCodecContext, videoCodec, NULL);
if (ret != 0) goto free_res;

讀取第一幀視頻I幀

AVPacket *firstPacket = av_packet_alloc();
AVFrame *rawFrame = av_frame_alloc();
while(av_read_frame(context, firstPacket) == 0) {
    if (firstPacket->stream_index == videoStreamIndex) {
        avcodec_send_packet(videoCodecContext, firstPacket);
        avcodec_receive_frame(videoCodecContext, rawFrame);
        if (rawFrame->pict_type == AV_PICTURE_TYPE_I) {
            break;
        }
    }
}

使用sws_scale縮放圖片并進行格式轉換

int width = rawFrame->width;
int height = rawFrame->height;
int bitsPerComponent = 8;
int bitsPerPixel = bitsPerComponent * 4;
int bytesPerRow = 4 * width;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSMutableData *rgbaData = [NSMutableData.alloc initWithLength:rawFrame->width * rawFrame->height * 4];
    
void *dstAddress = (void *)rgbaData.bytes;
//  使用sws處理圖片
struct SwsContext *swsContext = sws_getContext(rawFrame->width, rawFrame->height, rawFrame->format, rawFrame->width, rawFrame->height, AV_PIX_FMT_RGBA, SWS_BILINEAR, NULL, NULL, NULL);
sws_scale(swsContext,
              (const uint8_t *const *) rawFrame->data,
                rawFrame->linesize,
              0,
          rawFrame->height,
          (uint8_t *const *)&dstAddress,
          &bytesPerRow);

這里將AVFrame的圖片轉換成rgba的像素格式,數(shù)據(jù)存儲到rgbaData

rgba數(shù)據(jù)轉換成UIImage

CFDataRef rgbDataRef = (__bridge CFDataRef)rgbaData;
CGDataProviderRef provider = CGDataProviderCreateWithCFData(rgbDataRef);
CGImageRef cgImage = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpace, kCGImageAlphaLast | kCGBitmapByteOrderDefault, provider, NULL, YES, kCGRenderingIntentDefault);

UIImage *img = [UIImage.alloc initWithCGImage:cgImage];
CGImageRelease(cgImage);

UIImage轉換成NSData保存到本地

 NSData *imgData = UIImageJPEGRepresentation(img, 0.8);
[imgData writeToFile:destPath atomically:YES];

釋放相關對象

CGDataProviderRelease(provider);
CFRelease(colorSpace);
sws_freeContext(swsContext);

avcodec_free_context(&videoCodecContext);
av_packet_free(&firstPacket);
av_frame_free(&rawFrame);
avformat_free_context(context);
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容