前语
Android其实原生自带类
MediaMetadataRetriever
类,也可以依据时刻戳直接获取Bitmap
效果
示例
1.1 获取视频流信息
AVFormatContext * fmt_ctx = avformat_alloc_context();
int ret = avformat_open_input(&fmt_ctx, m_path, nullptr, nullptr);
if (ret < 0 || !fmt_ctx) {
exit(1);
}
ret = avformat_find_stream_info(fmt_ctx, nullptr);
if (ret < 0) {
exit(1);
}
for (int i = 0; i < fmt_ctx->nb_streams; i++) {
if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoIdx = i;
break;
}
}
video_parametres = fmt_ctx->streams[videoIdx]->codecpar;
-
avformat_open_input
初始化fmt_ctx并翻开获取文件句柄 -
avformat_find_stream_info
查找文件信息流,寻找视频索引
1.2 定位信息
uint8_t *seek_time_to_yuv(double percentage) {
AVRational time_base = fmt_ctx->streams[videoIdx]->time_base;
double duration = fmt_ctx->streams[videoIdx]->duration * av_q2d(time_base);
double timestamp = percentage * duration;
int64_t pts = timestamp / av_q2d(time_base);
int ret = av_seek_frame(fmt_ctx, videoIdx, pts, AVSEEK_FLAG_BACKWARD);
if (ret < 0) {
return nullptr;
}
AVPacket *pkt = av_packet_alloc();
while (av_read_frame(fmt_ctx, pkt) >= 0) {
if (pkt->stream_index == videoIdx) {
//发送到解码器
ret = avcodec_send_packet(decode_ctx, pkt);
if (ret < 0) {
return nullptr;
}
AVFrame *seek_frame = av_frame_alloc();
//取出解码后数据
ret = avcodec_receive_frame(decode_ctx, seek_frame);
if (ret < 0) {
return nullptr;
}
int width = video_parametres->width;
int height = video_parametres->height;
AVFrame *nv21_frame = av_frame_alloc();
uint8_t *out_buf = (uint8_t *) av_malloc(
av_image_get_buffer_size(AV_PIX_FMT_NV21, width,
height, 1));
av_image_fill_arrays(nv21_frame->data, nv21_frame->linesize, out_buf,
AV_PIX_FMT_NV21,
width, height, 1);
SwsContext *sws_ctx = sws_getContext(width, height,
(AVPixelFormat) video_parametres->format,
width, height,
AV_PIX_FMT_NV21, SWS_BICUBIC,
nullptr, nullptr, nullptr);
sws_scale(sws_ctx, seek_frame->data, seek_frame->linesize, 0, height,
nv21_frame->data, nv21_frame->linesize);
int y_size = width * height;
int uv_size = y_size / 4;
memcpy(out_buf, nv21_frame->data[0], y_size);
memcpy(out_buf + y_size, nv21_frame->data[1], uv_size * 2);
av_packet_free(&pkt);
av_frame_free(&nv21_frame);
return out_buf;
}
}
return nullptr;
}
- 核算视频总时长
duration
这取决于time_base
时刻基 - 依据传入的百分比核算出详细的
pts
显现的时刻在依据时刻基time_base
进行转化到时刻戳 -
av_seek_frame
定位到当时时刻:- AVFormatContext *s:输入的AVFormatContext结构体。
- int stream_index:流的索引。
- int64_t timestamp:时刻戳。
- int flags:标志位。
其中,AVFormatContext *s是输入的AVFormatContext结构体,包含了输入文件的格局信息。stream_index是流的索引,用于指定要定位的流。timestamp是时刻戳,用于指定要定位的时刻。flags是标志位,用于指定定位的方法。
-
AVSEEK_FLAG_BACKWARD
这个flag标志表明假如没有当时关键帧则定位到前一个 -
avcodec_send_packet
、avcodec_receive_frame
进行解码 -
sws_scale
将数据格局转化成AV_PIX_FMT_NV21
之所以转化成
AV_PIX_FMT_NV21
因为在Android中YuvImage类支撑NV21格局转化成Bitmap
Github
Android-AddImageWatermarkToVideo