FFmpeg c++ 如何截取视频片段
时间: 2023-11-27 13:03:19 浏览: 176
simplest_ffmpeg_transcoder.rar_ffmpeg C++builder
5星 · 资源好评率100%
在 C++ 中使用 FFmpeg 截取视频片段,可以按照以下步骤进行:
1. 引入 FFmpeg 头文件
```c++
extern "C" {
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
```
2. 打开视频文件
```c++
AVFormatContext *pFormatCtx = nullptr;
if (avformat_open_input(&pFormatCtx, filename, nullptr, nullptr) < 0) {
// 打开文件失败
return;
}
if (avformat_find_stream_info(pFormatCtx, nullptr) < 0) {
// 获取文件信息失败
return;
}
```
3. 查找视频流
```c++
int videoStream = -1;
for (unsigned int i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream = i;
break;
}
}
if (videoStream == -1) {
// 没有找到视频流
return;
}
```
4. 获取视频编解码器
```c++
AVCodecParameters *pCodecParams = pFormatCtx->streams[videoStream]->codecpar;
AVCodec *pCodec = avcodec_find_decoder(pCodecParams->codec_id);
if (pCodec == nullptr) {
// 没有找到对应的编解码器
return;
}
AVCodecContext *pCodecCtx = avcodec_alloc_context3(pCodec);
if (pCodecCtx == nullptr) {
// 分配编解码器上下文失败
return;
}
if (avcodec_parameters_to_context(pCodecCtx, pCodecParams) < 0) {
// 设置编解码器上下文参数失败
return;
}
if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0) {
// 打开编解码器失败
return;
}
```
5. 创建解码器帧和转换器
```c++
AVFrame *pFrame = av_frame_alloc();
if (pFrame == nullptr) {
// 分配解码器帧失败
return;
}
AVFrame *pFrameRGB = av_frame_alloc();
if (pFrameRGB == nullptr) {
// 分配转换器帧失败
return;
}
int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1);
uint8_t *buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
if (buffer == nullptr) {
// 分配转换器缓冲区失败
return;
}
av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, buffer, AV_PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height, 1);
struct SwsContext *swsCtx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB24, SWS_BILINEAR, nullptr, nullptr, nullptr);
if (swsCtx == nullptr) {
// 创建转换器失败
return;
}
```
6. 定位到开始截取的位置
```c++
int64_t startTimestamp = startTimeInSeconds * AV_TIME_BASE;
if (av_seek_frame(pFormatCtx, videoStream, startTimestamp, AVSEEK_FLAG_BACKWARD) < 0) {
// 定位到指定时间戳失败
return;
}
```
7. 读取视频帧并进行截取
```c++
int gotFrame = 0;
AVPacket packet;
while (av_read_frame(pFormatCtx, &packet) >= 0) {
if (packet.stream_index == videoStream) {
avcodec_decode_video2(pCodecCtx, pFrame, &gotFrame, &packet);
if (gotFrame) {
int64_t pts = av_frame_get_best_effort_timestamp(pFrame);
if (pts >= endTimestamp) {
break;
}
sws_scale(swsCtx, (uint8_t const *const *) pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
// 处理截取后的视频帧
}
}
av_packet_unref(&packet);
}
```
8. 释放资源
```c++
sws_freeContext(swsCtx);
av_frame_free(&pFrameRGB);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
```
其中,`startTimeInSeconds` 和 `endTimeInSeconds` 分别为要截取的视频片段的开始时间和结束时间,以秒为单位。`AV_TIME_BASE` 定义了时间戳的时间基,一般为 1 秒钟的微秒数(1000000)。`AVSEEK_FLAG_BACKWARD` 表示定位到离指定时间戳最近的关键帧之前的位置。
阅读全文