用ffmpeg 发送rtp怎么在iPhone上播放rtp传过来的H264码流

h264-请教关于ffmpeg解码rtp码流的问题
请教关于ffmpeg解码rtp码流的问题
小弟最近在学习网络视频流传输,有个疑问如下:用rtp流封装h264码流,rtp协议中有sequence和timestamp,我想请问这两者和PTS和DTS有关联吗?如果用ffmpeg解码,是否是按rtp包的有效数据加上0x扔给解码器就行?SPS以及PPS中的数据需要初始化给ffmpeg吗?
本人小白,希望各位多多指教,谢谢!
组包之后 Nal前面加上0x,
扔个解码器 就行 了他的最新文章
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)2012年4月 专题开发/技术/项目大版内专家分月排行榜第二2012年3月 专题开发/技术/项目大版内专家分月排行榜第二
2013年12月 C/C++大版内专家分月排行榜第二2013年12月 Linux/Unix社区大版内专家分月排行榜第二2013年11月 C/C++大版内专家分月排行榜第二2013年10月 C/C++大版内专家分月排行榜第二
匿名用户不能发表回复!|iOS使用ffmpeg播放rstp实时监控视频数据流
一、编译针对iOS平台的ffmpeg库(kxmovie)
近期有一个项目,需要播放各种格式的音频、视频以及网络摄像头实时监控的视频流数据,经过多种折腾之后,最后选择了kxmovie,kxmovie项目已经整合了ffmpeg和简单的播放器,具体可以参考kxmovie主页:&
编译kxmovie很简单,已经支持iOS
6.1&和&armv7s,一次成功,编译过程没出现什么问题:
git clone git://github.com/kolyvan/kxmovie.git
cd kxmovie
git submodule update --init&
二、使用kxmovie
1.把kxmovie/output文件夹下文件添加到工程
2.添加框架:MediaPlayer,
CoreAudio, AudioToolbox, Accelerate, QuartzCore, OpenGLES and
libz.dylib,libiconv.dylib
3.添加lib库:libkxmovie.a,
libavcodec.a, libavformat.a, libavutil.a, libswscale.a,
libswresample.a
4.播放视频:
ViewController *
vc = [KxMovieViewController movieViewControllerWithContentPath:path parameters:nil];
[self presentViewController:vc animated:YES completion:nil];&
5.具体使用参考demo工程:KxMovieExample
三、碰到的问题
播放本地视频和网络视频正常,播放网络摄像头实时监控视频流(h264)的时候出现错误:
[rtsp @ 0x906cc00] UDP timeout, retrying with
[rtsp @ 0x906cc00] Nonmatching transport in server
[rtsp @ 0x906cc00] Could not find codec parameters
for stream 0 (Video: h264): unspecified size
Consider increasing the value for the
'analyzeduration' and 'probesize' options
Couldn't find stream
information
跟踪代码,错误是在avformat_find_stream_info获取流信息失败的时候的时候触发。
if(avformat_find_stream_info(pFormatCtx,NULL) &
av_log(NULL,
AV_LOG_ERROR,
"Couldn't find stream information\n");
goto initE
经过几天的摸索,最终确定是网络的问题(在模拟器播放一直出错,在3G网络下能播放),具体原因估计是rstp视频流,程序默认采用udp传输或者组播,导致在私有网络视频流不能正常传输。
解决方法,把视频流的传输模式强制成tcp传输:
// Open video file
pFormatCtx =
avformat_alloc_context();&&
//有三种传输方式:tcp&udp_multicast
udp,强制采用tcp传输
AVDictionary*
options =&NULL;
av_dict_set(&options,
"rtsp_transport", "tcp", 0);
if(avformat_open_input(&pFormatCtx, [moviePath
cStringUsingEncoding:NSASCIIStringEncoding], &
&options) !=
av_log(NULL,
AV_LOG_ERROR,
"Couldn't open file\n");
goto initE
// Retrieve stream information
if(avformat_find_stream_info(pFormatCtx,NULL) &
av_log(NULL,
AV_LOG_ERROR,
"Couldn't find stream information\n");
goto initE
问题解决。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。在iOS平台使用ffmpeg解码h264视频流
时间: 00:32:58
&&&& 阅读:1042
&&&& 评论:
&&&& 收藏:0
标签:来源:
在iOS平台使用ffmpeg解码h264视频流,有需要的朋友可以参考下。
对于视频文件和rtsp之类的主流视频传输协议,ffmpeg提供avformat_open_input接口,直接将文件路径或URL传入即可打开。读取视频数据、初始参数设置等,都可以通过调用API来完成。
但是对于h264流,没有任何封装格式,也就无法使用libavformat。所以许多工作需要自己手工完成。
这里的h264流指AnnexB,也就是每个nal unit以起始码00 00 00 01 或 00 00 01开始的格式。关于h264码流格式,可以参考。
首先是手动设定AVCodec和AVCodecContext:
AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264);
AVCodecContext *codecCtx = avcodec_alloc_context3(codec);
avcodec_open2(codecCtx, codec, nil);
在AVCodecContext中会保存很多解码需要的信息,比如视频的长和宽,但是现在我们还不知道。
这些信息存储在h264流的SPS(序列参数集)和PPS(图像参数集)中。
对于每个nal unit,起始码后面第一个字节的后5位,代表这个nal unit的类型。7代表SPS,8代表PPS。一般在SPS和PPS后面的是IDR帧,无需前面帧的信息就可以,用5来代表。
检测nal unit类型的方法:
- (int)typeOfNalu:(NSData *)data
char first = *(char *)[data bytes];
return first & 0x1f;
264解码器在解码SPS和PPS的时候会提取出视频的信息,保存在AVCodecContext中。但是只把SPS和PPS传递进去是不行的,需要把后面的IDR帧一起传给解码器,才能够正确解码。
可以写一个简单的检测,如果接收到SPS,就把后面的PPS和IDR帧都接收过来,然后一起传给。
初始化一个AVPacket和AVFrame,然后把SPS、PPS、IDR帧连在一起的数据块传给AVPacket的data指针,再进行解码。
我们假设包含SPS、PPS、IDR帧的数据块保存在Data中,长度为len。
char *videoD
AVFrame *frame = av_frame_alloc();
av_new_packet(&packet, len);
memcpy(packet.data, videoData, len);
int ret, got_
ret = avcodec_decode_video2(codecCtx, frame, &got_picture, &packet);
if (ret & 0){
if(got_picture){
//进行下一步的处理
这样就可以顺利解码h264流了,解码出的数据保存在AVFrame中。
我写了一个Objective-C类用来执行接收视频流、、播放一系列步骤。
视频数据的接收采用socket直接接收,使用了项目。
就像项目名称中指明的,这是一个异步类。读写socket的动作会在一个单独的dispatch queue中执行,执行完毕后对应的delegate方法会自动调用,在其中进行进一步的处理。
读取h264流使用了GCDAsyncSocket 的
- (void)readDataToData:(NSData *) withTimeout:(NSTimeInterval)timeout
方法,也就是当读到和data中的字节一致的内容时就停止读取,并调用delegate方法。传入的data参数是 00 00 01
三个字节。这样每次读入的nalu开始是没有start code的,而最后面有下一个nalu的start
code。因此每次读取之后都会把末尾的start code 暂存,然后把主体接到上一次暂存的start code之后,构成完整的nalu。
videoPlayer.h:
//Player.h
#import &Foundation/Foundation.h&
@interface videoPlayer : NSObject
//Player.m
#import "videoPlayer.h"
#import "GCDAsyncSocket.h"
#import "libavcodec/avcodec.h"
#import "libswscale/swscale.h"
const int Header = 101;
const int Data = 102;
@interface videoPlayer () &GCDAsyncSocketDelegate&
GCDAsyncSocket *;
NSData *startcodeD
NSData *StartC
AVCodecContext *codecC
struct SwsContext *img_convert_
NSMutableData *keyF
int outputW
int outputH
@implementation Player
- (id)init
self = [super init];
if (self) {
avcodec_register_all();
frame = av_frame_alloc();
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
codecCtx = avcodec_alloc_context3(codec);
int ret = avcodec_open2(codecCtx, codec, nil);
if (ret != 0){
NSLog(@"open codec failed :%d",ret);
= [[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
keyFrame = [[NSMutableData alloc]init];
outputWidth = 320;
outputHeight = 240;
unsigned char startcode[] = {0,0,1};
startcodeData = [NSData WithBytes:startcode length:3];
- (void)startup
NSError *error =
[socket connectToHost:@"192.168.1.100"
onPort:9982
withTimeout:-1
error:&error];
NSLog(@"%@",error);
if (!error) {
[ readDataToData:startcodeData withTimeout:-1 tag:0];
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *) withTag:()tag
[socket readDataToData:startcodeData withTimeout:-1 tag:Data];
if(tag == Data){
int type = [self typeOfNalu:data];
if (type == 7 || type == 8 || type == 6 || type == 5) { //SPS PPS SEI IDR
[keyFrame appendData:StartCode];
[keyFrame appendBytes:[data bytes] length:[data length] - [self startCodeLenth:data]];
if (type == 5 || type == 1) {//IDR P frame
if (type == 5) {
int nalLen = (int)[keyFrame length];
av_new_packet(&packet, nalLen);
memcpy(packet., [keyFrame bytes], nalLen);
keyFrame = [[NSMutableData alloc] init];//reset keyframe
NSMutableData *nalu = [[NSMutableData alloc]initWithData:StartCode];
[nalu appendBytes:[data bytes] length:[data length] - [self startCodeLenth:data]];
int nalLen = (int)[nalu length];
av_new_packet(&packet, nalLen);
memcpy(packet., [nalu bytes], nalLen);
int ret, got_
//NSLog(@"decode start");
ret = avcodec_decode_2(codecCtx, frame, &got_picture, &packet);
//NSLog(@"decode ");
if (ret & 0) {
NSLog(@"decode error");
if (!got_picture) {
NSLog(@"didn‘t get picture");
static int sws_flags =
SWS_FAST_BILINEAR;
//outputWidth = codecCtx-&
//outputHeight = codecCtx-&
if (!img_convert_ctx)
img_convert_ctx = sws_getContext(codecCtx-&width,
codecCtx-&height,
codecCtx-&pix_fmt,
outputWidth,
outputHeight,
PIX_FMT_YUV420P,
sws_flags, NULL, NULL, NULL);
avpicture_alloc(&picture, PIX_FMT_YUV420P, outputWidth, outputHeight);
ret = sws_scale(img_convert_ctx, (const uint8_t* const*)frame-&, frame-&linesize, 0, frame-&height, picture.data, picture.linesize);
[self display];
//NSLog(@"show frame ");
avpicture_free(&picture);
av_free_packet(&packet);
[self saveStartCode:data];
- (void)display
- (int)typeOfNalu:(NSData *)data
char first = *(char *)[data bytes];
return first & 0x1f;
- (int)startCodeLenth:(NSData *)
char temp = *((char *)[data bytes] + [data length] - 4);
return temp == 0x00 ? 4 : 3;
- (void)saveStartCode:(NSData *)data
int startCodeLen = [self startCodeLenth:data];
NSRange startCodeRange = {[data length] - startCodeLen, startCodeLen};
StartCode = [data subdataWithRange:startCodeRange];
- (void)shutdown
if()[socket disconnect];
- (void)dealloc
// Free scaler
if(img_convert_ctx)sws_freeContext(img_convert_ctx);
// Free the YUV frame
if(frame)av_frame_free(&frame);
// Close the codec
if (codecCtx) avcodec_close(codecCtx);
在项目中播放出来的YUV视频使用了OPENGL,这里播放的部分就略去了。
标签:原文地址:http://www.cnblogs.com/sunminmin/p/4976224.html
&&国之画&&&& &&&&chrome插件&&
版权所有 京ICP备号-2
迷上了代码!

我要回帖

更多关于 ffmpeg rtp 组播 的文章

 

随机推荐