嗨,我需要一些帮助/指导,因为我陷入了研究中。
问题:
如何在API(通过编程)或控制台版本中使用gstreamer或avlib(ffmpeg)转换RTP数据。
数据
我有来自RTP / RTCP的RTP转储,因此我可以获得文件中每个RTP数据包的精确启动和停止。这是一个H264视频流转储。 数据是这样的,因为我需要通过libcurl获取RTCP / RTP交错流(我现在正在做)
状态
我尝试使用ffmpeg来使用纯RTP数据包,但似乎通过控制台或编程使用rtp涉及在ffmpeg中“启动”整个rtsp / rtp会话业务。我已经停在那里,暂时我没有更深入地追求这条道路。我想这对于像ff_rtp_parse_packet()
这样的情人级RTP API是可行的。我对这个lib太新了直接做到了。
然后有gstreamer它有更多的功能可以在没有编程的情况下完成它,但暂时我无法弄清楚如何将它传递给我的RTP转储。
我还试图做一点诡计并通过 socat / nc 将转储流转到udp端口并通过ffplay以sdp文件作为输入来监听它,似乎rtp至少得到认可是一些进步,但是对于 socat ,有大量的数据包缺失(数据发送得太快了吗?),最后数据不可视化。当我使用 nc 时,视频严重失误,但至少没有那么多收到错误。
数据未正确显示的方式。
我知道我可以“手动”拆分数据但是想法是通过某种类型的库来实现,因为最后还会有第二个带有音频的流,它必须与视频一起复用。 / p>
我很感激有关如何解决这个问题的任何帮助。 感谢。
答案 0 :(得分:2)
经过一段时间后,我有时间再次坐下来讨论这个问题,最后我得到了满足我的解决方案。我继续使用RTP交叉流(RTP通过单个TCP连接与RTCP交错)
所以我有一个交错的RTCP / RTP流需要被拆分为音频(PCM A-Law)和视频(h.264约束基线)RTP数据包。
此处描述了包含RTP数据的RTSP流的分解rfc2326
这里描述了H264的解包[{3}},对于PCM A-Law,帧在RTP中是原始音频,因此不需要拆包。
下一步是为每个流计算正确的PTS(或演示时间戳),这有点麻烦但最后Live555代码来帮助 (见rfc6184) 最后一项任务是将其复制到一个支持PCM alaw的容器中,我使用了ffmpeg的avlibraries。 互联网上有很多例子,但其中很多都已经过时了(ffmpeg在API变更区域非常'动态')所以我发布了(最重要的部分)最终实际上对我有用的东西:
设置部分:
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
AVFormatContext *formatContext;
AVOutputFormat *outputFormat;
AVStream *video_st;
AVStream *audio_st;
AVCodec *av_encode_codec = NULL;
AVCodec *av_audio_encode_codec = NULL;
AVCodecContext *av_video_encode_codec_ctx = NULL;
AVCodecContext *av_audio_encode_codec_ctx = NULL;
av_register_all();
av_log_set_level(AV_LOG_TRACE);
outputFormat = av_guess_format(NULL, pu8outFileName, NULL);
outputFormat->video_codec = AV_CODEC_ID_H264;
av_encode_codec = avcodec_find_encoder(AV_CODEC_ID_H264);
av_audio_encode_codec = avcodec_find_encoder(AV_CODEC_ID_PCM_ALAW);
avformat_alloc_output_context2(&formatContext, NULL, NULL, pu8outFileName);
formatContext->oformat = outputFormat;
strcpy(formatContext->filename, pu8outFileName);
outputFormat->audio_codec = AV_CODEC_ID_PCM_ALAW;
av_video_encode_codec_ctx = avcodec_alloc_context3(av_encode_codec);
av_audio_encode_codec_ctx = avcodec_alloc_context3(av_audio_encode_codec);
av_video_encode_codec_ctx->codec_id = outputFormat->video_codec;
av_video_encode_codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
av_video_encode_codec_ctx->bit_rate = 4000;
av_video_encode_codec_ctx->width = u32width;
av_video_encode_codec_ctx->height = u32height;
av_video_encode_codec_ctx->time_base = (AVRational){ 1, u8fps };
av_video_encode_codec_ctx->max_b_frames = 0;
av_video_encode_codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
av_audio_encode_codec_ctx->sample_fmt = AV_SAMPLE_FMT_S16;
av_audio_encode_codec_ctx->codec_id = AV_CODEC_ID_PCM_ALAW;
av_audio_encode_codec_ctx->codec_type = AVMEDIA_TYPE_AUDIO;
av_audio_encode_codec_ctx->sample_rate = 8000;
av_audio_encode_codec_ctx->channels = 1;
av_audio_encode_codec_ctx->time_base = (AVRational){ 1, u8fps };
av_audio_encode_codec_ctx->channel_layout = AV_CH_LAYOUT_MONO;
video_st = avformat_new_stream(formatContext, av_encode_codec);
audio_st = avformat_new_stream(formatContext, av_audio_encode_codec);
audio_st->index = 1;
video_st->avg_frame_rate = (AVRational){ 90000, 90000 / u8fps };
av_stream_set_r_frame_rate(video_st, (AVRational){ 90000, 90000 / u8fps });
视频数据包的写法如下:
uint8_t *pu8framePtr = video_frame;
AVPacket pkt = { 0 };
av_init_packet(&pkt);
if (0x65 == pu8framePtr[4] || 0x67 == pu8framePtr[4] || 0x68 == pu8framePtr[4])
{
pkt.flags = AV_PKT_FLAG_KEY;
}
pkt.data = (uint8_t *)pu8framePtr;
pkt.size = u32LastFrameSize;
pkt.pts = av_rescale_q(s_video_sync.fSyncTime.tv_sec * 1000000 + s_video_sync.fSyncTime.tv_usec, (AVRational){ 1, 1000000 }, video_st->time_base);
pkt.dts = pkt.pts;
pkt.stream_index = video_st->index;
av_interleaved_write_frame(formatContext, &pkt);
av_packet_unref(&pkt);
,对于这样的音频:
AVPacket pkt = { 0 };
av_init_packet(&pkt);
pkt.flags = AV_PKT_FLAG_KEY;
pkt.data = (uint8_t *)pu8framePtr;
pkt.size = u32AudioDataLen;
pkt.pts = av_rescale_q(s_audio_sync.fSyncTime.tv_sec * 1000000 + s_audio_sync.fSyncTime.tv_usec, (AVRational){ 1, 1000000 }, audio_st->time_base);
pkt.dts = pkt.pts;
pkt.stream_index = audio_st->index;
if (u8FirstIFrameFound) {av_interleaved_write_frame(formatContext, &pkt);}
av_packet_unref(&pkt)
最后有些内容:
av_write_trailer(formatContext);
av_dump_format(formatContext, 0, pu8outFileName, 1);
avcodec_free_context(&av_video_encode_codec_ctx);
avcodec_free_context(&av_audio_encode_codec_ctx);
avio_closep(&formatContext->pb);
avformat_free_context(formatContext);