背景
因为项目需要,得从外部获取视频264和音频PCM或者g711编解码数据,然后发布成rtmp出来。目前,网上似乎找不到这样的文章,很多发布RTMP都是用从音视频文件读取后再发布出来,因此记录分享,若有不对的地方,欢迎批评指出。
思路
做这个项目,大体思路是参考FFmpeg的muxing.c。muxing.c主要做的就是输出一个音视频格式,其中它的音频和视频数据都是通过算法生成原始的数据(YUV和PCM),然后再做编码,最后封装成音视频格式。大概的步骤就是:根据输出文件后缀获取音频和视频的编解码ID、填充音视频编码信息、获取音视频原始数据、编解音视频数据、音视频同步操作、音视频封装等。
实施
muxing.c里面的有些视频编码和音频解码我是不需要用到的,所以这部分功能我删减掉了,我这边只要获取到完整的一帧H264视频数据和完整一个待重采样的PCM音频数据(PCM重采样并编码成AAC数据)就可以开始做封装格式的工作。
类的主体内容如下:
typedef struct OutputStream {
AVStream *st;
AVCodecContext *enc;
int64_t next_pts;
long long samples_count;
AVFrame *frame;
AVFrame *tmp_frame;
struct SwrContext *swr_ctx;
} OutputStream;
class RtmpStream
{
public:
RtmpStream();
virtual ~RtmpStream();
int init(const char *filename);
int writedata(AVMediaType datatype, char *data, int datalen);
private:
int write_frame(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt);
int add_stream(OutputStream *ost, AVCodec **codec, enum AVCodecID codec_id);
int open_audio(AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg);
int write_audio_frame(OutputStream *ost, char* data, int datalen);
int open_video(AVCodec *codec, OutputStream *ost, AVDictionary *opt_arg);
int write_video_frame(OutputStream *ost, char* data, int datalen);
void close_stream(OutputStream *ost);
bool isIdrFrame2(uint8_t *buf, int len);
bool isIdrFrame1(uint8_t *buf, int size);
private:
OutputStream video_st, audio_st;
AVOutputFormat *fmt;
AVFormatContext *oc;
AVCodec *audio_codec, *video_codec;
AVDictionary *opt;
int have_video, have_audio;
int ptsInc;
int iRawLineSize;
int iRawBuffSize;
uint8_t *pRawBuff;
int iConvertLineSize;
int iConvertBuffSize;
uint8_t *pConvertBuff;
char pcmencodebuf[4096];
int pcmencodesize;
};
接口简单介绍:
init:初始化输出格式信息;
add_stream:添加音视频的编解码参数和通道信息;
open_audio:初始化音频通道信息、打开音频编码器、重采样初始化等;
open_video:初始化视频通道信息;
write_audio_frame:音频重采样、音频编码、编码数据写入等;
write_video_frame:视频编码数据写入;
接口调用
内存H264+PCM发布rtmp
RtmpStream* prtmpstream = NULL;
prtmpstream = new RtmpStream;
//filename可以是后缀名flv文件名也可以是rtmp地址
prtmpstream->init(filename);
//写视频输入的数据是一帧完整的H264数据、写视频是一帧完整的PCM数据
prtmpstream->writedata(AVMEDIA_TYPE_VIDEO, h264buffer, iPsLength);
prtmpstream->writedata(AVMEDIA_TYPE_AUDIO, (char *)audioframe->data[0], audioframe->linesize[0]);
delete prtmpstream;
遇到难点
音频重采样
待重采样的音频数据是要指定固定的采样长度的,不然重采样后再编码的音频播放出来的会是错误的数据。
iRawBuffSize = av_samples_get_buffer_size(&iRawLineSize, c->channels, nb_samples, AV_SAMPLE_FMT_S16, 0);
pRawBuff = (uint8_t *)av_malloc(iRawBuffSize);
ost->tmp_frame = av_frame_alloc();
ost->tmp_frame->nb_samples = nb_samples;
ost->tmp_frame->format = AV_SAMPLE_FMT_S16;
ost->tmp_frame->channels = c->channels;
ret = avcodec_fill_audio_frame(ost->tmp_frame, c->channels, AV_SAMPLE_FMT_S16, (const uint8_t*)pRawBuff, iRawBuffSize, 0);
if (ret<0)
{
printf("Could not fill input audio frame\n");
return -1;
}
iRawBuffSize 这个就是送入重采样的PCM数据长度。
下载
源码下载:https://download.csdn.net/download/qq_22633333/11617399