FFmpeg 解码视频
[TOC]
自动探测文件格式
从文件解码
AVFormatContext *formatCtx = avformat_alloc_context();
auto ret = avformat_open_input(&formatCtx, "1.h264", nullptr, nullptr);
if (ret < 0) {
// error
}
ret = avformat_find_stream_info(formatCtx, nullptr);
int streamIndex = -1;
for (int i = 0; i < (int)formatCtx->nb_streams; ++i) {
if (formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
streamIndex = i;
break;
}
}
if(streamIndex == 0) {
// error
}
auto *decodeParam = formatCtx->streams[streamIndex]->codecpar;
auto *decoder = avcodec_find_decoder(decodeParam->codec_id);
auto *decoderCtx = avcodec_alloc_context3(decoder);
ret = avcodec_parameters_to_context(decoderCtx, decodeParam);
ret = avcodec_open2(decoderCtx, decoder, nullptr);
auto *yuvFrame = av_frame_alloc();
auto *packet = av_packet_alloc();
while (av_read_frame(formatCtx, packet) >= 0) {
if (packet->stream_index != streamIndex) {
continue;
}
ret = avcodec_send_packet(decoderCtx, packet);
ret = avcodec_receive_frame(decoderCtx, yuvFrame);
if (ret == 0) {
// do sth
}
}
// 送空指针表示解码结束了
ret = avcodec_send_packet(decoderCtx, nullptr);
// 接收剩余解码数据
while (avcodec_receive_frame(decoderCtx, yuvFrame) == 0) {
// do sth
}
// clear
从内存解码
// opaque为avio_alloc_context的opaque参数
int FillData(void *opaque, uint8_t *buf, int buf_size) {
if (bufferIndex >= bufferSize) {
return AVERROR_EOF;
}
int cpLen;
if (bufferSize - bufferIndex < buf_size) {
cpLen = bufferSize - bufferIndex;
} else {
cpLen = buf_size;
}
bufferIndex += cpLen;
memcpy(buf, opaque, static_cast<size_t>(cpLen));
return cpLen;
}
void VideoToJpegBuff() {
auto *file = fopen("test.h265", "rb");
int ret = fseek(file, 0, SEEK_END);
if (ret < 0) {}
int size = (int)ftell(file);
bufferSize = size;
ret = fseek(file, 0, SEEK_SET);
auto *dataBuffer = new uint8_t[size];
ret = fread(dataBuffer, 1, size, file);
fclose(file);
auto *formatCtx = avformat_alloc_context();
// 给FFmpeg内部使用的buffer
auto *buffer = static_cast<unsigned char *>(av_malloc(1024 * 1024 * 5));
formatCtx->pb = avio_alloc_context(buffer, 1024 * 1024 * 5, 0, dataBuffer, FillData, nullptr, nullptr);
ret = avformat_open_input(&formatCtx, nullptr, nullptr, nullptr);
if (ret != 0) {}
ret = avformat_find_stream_info(formatCtx, nullptr);
int streamIndex = -1;
for (int i = 0; i < (int)formatCtx->nb_streams; ++i) {
if (formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
streamIndex = i;
break;
}
}
auto *decodeParam = formatCtx->streams[streamIndex]->codecpar;
auto *decoder = avcodec_find_decoder(decodeParam->codec_id);
auto *decoderCtx = avcodec_alloc_context3(decoder);
decoderCtx->thread_count = 20;
ret = avcodec_parameters_to_context(decoderCtx, decodeParam);
ret = avcodec_open2(decoderCtx, decoder, nullptr);
auto *yuvFrame = av_frame_alloc();
auto *packet = av_packet_alloc();
while (av_read_frame(formatCtx, packet) >= 0) {
if (packet->stream_index != streamIndex) {
continue;
}
ret = avcodec_send_packet(decoderCtx, packet);
ret = avcodec_receive_frame(decoderCtx, yuvFrame);
if (ret == 0) {
// do sth
}
}
ret = avcodec_send_packet(decoderCtx, nullptr);
while (avcodec_receive_frame(decoderCtx, yuvFrame) == 0) {
// do sth
}
// clear
}
保存为 JPEG
void Save2Jpeg(AVFrame *frame) {
auto *encoder = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
auto *encodeCtx = avcodec_alloc_context3(encoder);
encodeCtx->width = frame->width;
encodeCtx->height = frame->height;
encodeCtx->time_base = {1, 25};
encodeCtx->pix_fmt = AV_PIX_FMT_YUVJ420P;
int ret = avcodec_open2(encodeCtx, encoder, nullptr);
if (ret < 0) {
// error
}
auto *swsCtx = sws_getContext(frame->width, frame->height, static_cast<AVPixelFormat>(frame->format),
encodeCtx->width, encodeCtx->height, AV_PIX_FMT_YUVJ420P,
SWS_POINT, nullptr, nullptr, nullptr);
auto *jpegFrame = av_frame_alloc();
jpegFrame->height = frame->height;
jpegFrame->width = frame->width;
jpegFrame->format = AV_PIX_FMT_YUVJ420P;
ret = av_frame_get_buffer(jpegFrame, 0);
ret = sws_scale_frame(swsCtx, jpegFrame, frame);
auto *packet = av_packet_alloc();
ret = avcodec_send_frame(encodeCtx, frame);
// 编码结束
ret = avcodec_send_frame(encodeCtx, nullptr);
ret = avcodec_receive_packet(encodeCtx, packet);
auto *file = fopen("111.jpeg", "wb+");
fwrite(packet->data, 1, static_cast<size_t>(packet->size), file);
fclose(file);
// clear
}
由于个人水平有限,文中若有不合理或不正确的地方欢迎指出改正