FFmpeg 从零开始开发简单的音视频播放器(四)
程序员文章站
2022-07-01 17:39:50
...
视频转码和解码
一、开门见山
代码注释里对逻辑做了简单说明,多余的解释我就不说了,直接上代码:
// FFmpegDll.cpp: 定义控制台应用程序的入口点。
//
#include "stdafx.h"
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "libswresample/swresample.h"
}
AVFormatContext *fmt_ctx;
//流队列中,视频流所在的位置
int video_index = -1;
//视频解码上下文
AVCodecContext *video_codec_ctx;
//输出缓存大小
int video_out_buffer_size;
//输出缓存
uint8_t *video_out_buffer;
//转码后输出的视频帧(如yuv转rgb24)
AVFrame *video_out_frame = av_frame_alloc();
//格式转换上下文
struct SwsContext *video_convert_ctx;
//解码前数据包
AVPacket *packet = (AVPacket *)malloc(sizeof(AVPacket));
//初始化FFmpeg
//@param *url 媒体地址(本地/网络地址)
int init_ffmpeg(char *url) {
av_register_all();//注册组件
avformat_network_init();//支持网络流
fmt_ctx = avformat_alloc_context();
//打开文件
if (avformat_open_input(&fmt_ctx, url, NULL, NULL) != 0) {
return -1;
}
//查找流信息
if (avformat_find_stream_info(fmt_ctx, NULL) < 0)
{
return -1;
}
//找到流队列中,视频流所在位置
for (int i = 0; i < fmt_ctx->nb_streams; i++) {
if (fmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
video_index = i;
break;
}
}
//视频流没有找到
if (video_index == -1)
{
return -1;
}
//查找解码器
video_codec_ctx = fmt_ctx->streams[video_index]->codec;
AVCodec *video_codec = avcodec_find_decoder(video_codec_ctx->codec_id);
//解码器没有找到
if (video_codec == NULL)
{
return -1;
}
//打开解码器
if (avcodec_open2(video_codec_ctx, video_codec, NULL) < 0)
{
return -1;
}
//计算输出缓存
video_out_buffer_size = av_image_get_buffer_size(
AV_PIX_FMT_RGB24,
video_codec_ctx->width,
video_codec_ctx->height,
1);
//输出缓存
video_out_buffer = new uint8_t[video_out_buffer_size];
//准备一些参数,在视频格式转换后,参数将被设置值
av_image_fill_arrays(
video_out_frame->data,//转换后的数据
video_out_frame->linesize,
video_out_buffer, //视频buffer
AV_PIX_FMT_RGB24,//像素格式
video_codec_ctx->width,
video_codec_ctx->height,
1);
video_convert_ctx = sws_getContext(//图片格式转换上下文
video_codec_ctx->width,
video_codec_ctx->height,
video_codec_ctx->pix_fmt,
video_codec_ctx->width,
video_codec_ctx->height,
AV_PIX_FMT_RGB24,//转码为RGB像素
SWS_BICUBIC,
NULL, NULL, NULL);
av_init_packet(packet);
return 0;
}
//读取一帧
int read_frame() {
int ret = -1;
//是否从packet中解出一帧,0为未解出
int got_picture;
//从packet中解出来的原始视频帧
AVFrame *original_video_frame = av_frame_alloc();
if (av_read_frame(fmt_ctx, packet) == 0) {
if (packet->stream_index == video_index)
{
//解码。输入为packet,输出为original_video_frame
if (avcodec_decode_video2(video_codec_ctx, original_video_frame, &got_picture, packet) >= 0)
{
if (got_picture)
{
//图片格式转换(上面图片转换准备的参数,在这里使用)
sws_scale(video_convert_ctx,//图片转码上下文
(const uint8_t* const*)original_video_frame->data,//原始数据
original_video_frame->linesize,//原始参数
0,//转码开始游标,一般为0
video_codec_ctx->height,//行数
video_out_frame->data,//转码后的数据
video_out_frame->linesize);
ret = 1;
}
}
}
}
av_free_packet(packet);
av_free(original_video_frame);
return ret;
}
//获取视频缓存大小
int get_video_buffer_size() {
return video_out_buffer_size;
}
//获取视频帧
char *get_video_frame() {
return (char *)video_out_buffer;
}
//获取视频宽度
int get_video_width() {
return video_codec_ctx->width;
}
//获取视频高度
int get_video_height() {
return video_codec_ctx->height;
}
//释放资源
void release() {
sws_freeContext(video_convert_ctx);
av_free(video_out_frame);
av_free(video_out_buffer);
avcodec_close(video_codec_ctx);
avformat_close_input(&fmt_ctx);
}
int main()
{
char url[] = "rtmp://live.hkstv.hk.lxdns.com/live/hks";
int init_ret = init_ffmpeg(url);
if (init_ret >= 0)
{
while (read_frame() < 0)//读取一帧,直到读取到数据
{
printf("未读取到数据\n");
}
printf("高度:%d 宽度:%d 缓存大小:%d", get_video_height(), get_video_width(), get_video_buffer_size());
printf("\n=================================\n");
printf(get_video_frame());//打印出来,虽然打印出来的东西看不懂,但是证明已经获取到一帧的数据了
}
else
{
printf("初始化失败!");
}
getchar();
return 0;
}
二、运行项目
1、右击c++项目,“重新生成”:
------------------------------------------------------
2、运行项目,如下图:
----------------------------------------------------------------------
如果出现上图所示结果,证明已经读取到了一帧视频,这一帧视频的宽高是480x288,占用大小为414720字节。
注意:如果在生成项目过程中,出现4996错误,是由于vs不建议使用一些库导致的错误,解决办法如下图:
---------------------------------------------------------------------------------
上一篇: TCP/IP理解
下一篇: WebRTC研究:丢包判断