最简单的GB28181视频PS流播放器。
程序员文章站
2022-03-17 14:13:20
...
一 从PS流中提取h264和aac。
移步:https://blog.csdn.net/qq_39805297/article/details/107083322
二 基于ffmpeg解码h264获取rgb图像,解码aac成pcm格式。
bool H264Decoder::init()
{
av_register_all();
_pCodecContext = avcodec_alloc_context3(NULL);
_pH264VideoDecoder = avcodec_find_decoder(AV_CODEC_ID_H264);
if (_pH264VideoDecoder == NULL)
{
return false;
}
//初始化参数,下面的参数应该由具体的业务决定 AV_PIX_FMT_YUV420P;
_pCodecContext->time_base.num = 1;
_pCodecContext->frame_number = 1; //每包一个视频帧
_pCodecContext->codec_type = AVMEDIA_TYPE_VIDEO;
_pCodecContext->bit_rate = 0;
_pCodecContext->time_base.den = 25;//帧率
_pCodecContext->width = 0;//视频宽
_pCodecContext->height = 0;//视频高
_pCodecContext->pix_fmt = AV_PIX_FMT_YUVJ420P;
_pCodecContext->color_range = AVCOL_RANGE_MPEG;
if (avcodec_open2(_pCodecContext, _pH264VideoDecoder, NULL) < 0)
return false;
_pFrame = av_frame_alloc();//存储解码后AVFrame
_pFrameRGB = nullptr;
int ret, got_picture;
int y_size = _pCodecContext->width * _pCodecContext->height;
AVPacket *packet = (AVPacket *)malloc(sizeof(AVPacket));//存储解码前数据包AVPacket
av_new_packet(packet, y_size);
return true;
}
void H264Decoder::decodeFrame(uint8_t* buffer, uint32_t bufferLen)
{
AVPacket packet = { 0 };
packet.data = buffer; //这里填入一个指向完整H264数据帧的指针
packet.size = bufferLen; //这个填入H264数据帧的大小
int ret = avcodec_send_packet(_pCodecContext, &packet);
int got_picture = avcodec_receive_frame(_pCodecContext, _pFrame); //got_picture = 0 success, a frame was returned
if (ret < 0)
{
return;
}
if (got_picture == 0)
{
//像素格式转换。pFrame转换为pFrameRGB。
if (!_pFrameRGB)
{
_pFrameRGB = av_frame_alloc();
uint8_t *video_buffer;
video_buffer = new uint8_t[avpicture_get_size(AV_PIX_FMT_RGB32, _pCodecContext->width, _pCodecContext->height)];//分配AVFrame所需内存
av_image_fill_arrays(_pFrameRGB->data, _pFrameRGB->linesize, video_buffer, AV_PIX_FMT_RGB32, _pCodecContext->width, _pCodecContext->height, 1);
_imgConvertCtx = sws_getContext(_pCodecContext->width, _pCodecContext->height, _pCodecContext->pix_fmt,
_pCodecContext->width, _pCodecContext->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
}
sws_scale(_imgConvertCtx, (const uint8_t* const*)_pFrame->data, _pFrame->linesize, 0, _pCodecContext->height, _pFrameRGB->data, _pFrameRGB->linesize);
//------------显示--------
QImage img((uchar*)_pFrameRGB->data[0], _pCodecContext->width, _pCodecContext->height, QImage::Format_RGB32);
frameChanged(img);
}
}
void AACDecoder::aac_decoder_create(int sample_rate, int channels, int bit_rate)
{
AVCodec *pCodec = avcodec_find_decoder(AV_CODEC_ID_AAC);
if (pCodec == NULL)
{
printf("find aac decoder error\r\n");
return ;
}
// 创建显示contedxt
m_pCodecCtx = avcodec_alloc_context3(pCodec);
if (m_pCodecCtx == nullptr)
{
printf("can not alloc codecContext\r\n");
return;
}
m_pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
m_pCodecCtx->frame_number = 1;
m_pCodecCtx->sample_rate = sample_rate;
m_pCodecCtx->channels = channels;
m_pCodecCtx->bit_rate = bit_rate;
m_pCodecCtx->channel_layout = AV_CH_LAYOUT_STEREO;
if (avcodec_open2(m_pCodecCtx, pCodec, NULL) < 0)
{
printf("open codec error\r\n");
return ;
}
m_pFrame = av_frame_alloc();
uint64_t out_channel_layout = channels < 2 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO;
int out_nb_samples = 1024;
enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
m_au_convert_ctx = swr_alloc();
m_au_convert_ctx = swr_alloc_set_opts(m_au_convert_ctx, out_channel_layout, out_sample_fmt, sample_rate,
out_channel_layout, AV_SAMPLE_FMT_FLTP, sample_rate, 0, NULL);
swr_init(m_au_convert_ctx);
int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);
m_out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, out_sample_fmt, 1);
}
int AACDecoder::aac_decode_frame(uint8_t *pData, int nLen)
{
AVPacket packet;
av_init_packet(&packet);
packet.size = nLen;
packet.data = pData;
uint8_t *out_buffer = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE * 2);
if (packet.size > 0)
{
int ret = avcodec_send_packet(m_pCodecCtx, &packet);
int got_picture = avcodec_receive_frame(m_pCodecCtx, m_pFrame);
if (ret >= 0)
{
if (got_picture == 0)
{
ret = swr_convert(m_au_convert_ctx, &out_buffer, MAX_AUDIO_FRAME_SIZE, (const uint8_t **)m_pFrame->data, m_pFrame->nb_samples);
frameChanged(QByteArray((char*)out_buffer, m_out_buffer_size));
}
}
else
printf("avcodec_decode_audio4 %d sameles = %d outSize = %d\r\n", ret, m_pFrame->nb_samples, m_out_buffer_size);
}
av_free(out_buffer);
av_free_packet(&packet);
return 0;
}
三 使用Qt进行播放。
整体的源码已上传至github:https://github.com/cdebug/GBPlayer
效果图如下: