当前位置:  编程技术>移动开发
本页文章导读:
    ▪UITableView-的cell 自定义 高度,式样        UITableView-的cell 自定义 高度,样式- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReus.........
    ▪ QT 图片浏览器(2)        QT 图片浏览器(二)前边所写的QT图片浏览器程序是在window下QT Creator环境中编写的,在window上面运行效果很正确,但是因为是需要在linux下面的QT环境中编译,所以在linux下面编译以后运行出.........
    ▪ H264视频透过RTMP发送       H264视频通过RTMP发送       前面的文章中提到了通过RTSP(Real Time Streaming Protocol)的方式来实现视频的直播,但RTSP方式的一个弊端是如果需要支持客户端通过网页来访问,就需要在在页面中.........

[1]UITableView-的cell 自定义 高度,式样
    来源: 互联网  发布时间: 2014-02-18
UITableView-的cell 自定义 高度,样式

UITableView的强大更多程度上来自于可以任意自定义UITableViewCell单元格。通常,UITableView中的Cell是动态的,在使用过程中,会创建一个Cell池,根据每个cell的高度(即tableView:heightForRowAtIndexPath:返回值),以及屏幕高度计算屏幕中可显示几个cell。而进行自定义TableViewCell无非是采用代码实现或采用IB编辑nib文件来实现两种方式,本文主要收集代码的方式实现各种cell自定义。

如何动态调整Cell高度

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    static NSString *CellIdentifier = @"Cell";
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
        label.tag = 1;
        label.lineBreakMode = UILineBreakModeWordWrap;
        label.highlightedTextColor = [UIColor whiteColor];
        label.numberOfLines = 0;
        label.opaque = NO; // 选中Opaque表示视图后面的任何内容都不应该绘制
        label.backgroundColor = [UIColor clearColor];
        [cell.contentView addSubview:label];
        [label release];
    }
 
    UILabel *label = (UILabel *)[cell viewWithTag:1];
    NSString *text;
    text = [textArray objectAtIndex:indexPath.row];
    CGRect cellFrame = [cell frame];
    cellFrame.origin = CGPointMake(0, 0);
 
    label.text = text;
    CGRect rect = CGRectInset(cellFrame, 2, 2);
    label.frame = rect;
    [label sizeToFit];
    if (label.frame.size.height > 46) {
        cellFrame.size.height = 50 + label.frame.size.height - 46;
    }
    else {
        cellFrame.size.height = 50;
    }
    [cell setFrame:cellFrame];
 
    return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
    return cell.frame.size.height;
}

如何用图片自定义Table Separeator分割线
一般地,利用类似[tableView setSeparatorColor:[UIColor redColor]];语句即可修改cell中间分割线的颜色。那又如何用一个图片作为分割线背景呢?可以尝试如下:
方法一:
先设置cell separatorColor为clear,然后把图片做的分割线添加到自定义的custom cell上。

方法二:
在cell里添加一个像素的imageView后将图片载入进,之后设置tableView.separatorStyle = UITableViewCellSeparatorStyleNone

自定义首行Cell与其上面导航栏间距

tableView.tableHeaderView = [[[UIView alloc] initWithFrame:CGRectMake(0,0,5,20)] autorelease];

自定义UITableViewCell的accessory样式
      默认的accessoryType属性有四种取值:UITableViewCellAccessoryNone、UITableViewCellAccessoryDisclosureIndicator、UITableViewCellAccessoryDetailDisclosureButton、UITableViewCellAccessoryCheckmark。如果想使用自定义附件按钮的其他样式,则需使用UITableView的accessoryView属性来指定。

UIButton *button;
if(isEditableOrNot) {
    UIImage *image = [UIImage imageNamed:@"delete.png"];
    button = [UIButton buttonWithType:UIButtonTypeCustom];
    CGRect frame = CGRectMake(0.0,0.0,image.size.width,image.size.height);
    button.frame = frame;
    [button setBackgroundImage:image forState:UIControlStateNormal];
    button.backgroundColor = [UIColor clearColor];
    cell.accessoryView = button;
}else{
    button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.backgroundColor = [UIColor clearColor];
    cell.accessoryView = button;
}

以上代码仅仅是定义了附件按钮两种状态下的样式,问题是现在这个自定义附件按钮的事件仍不可用。即事件还无法传递到UITableViewDelegate的accessoryButtonTappedForRowWithIndexPath方法上。当我们在上述代码中在加入以下语句:
             [button addTarget:self action:@selector(btnClicked:event:) forControlEvents:UIControlEventTouchUpInside];
后,虽然可以捕捉到每个附件按钮的点击事件,但我们还无法进行区别到底是哪一行的附件按钮发生了点击动作!因为addTarget:方法最多允许传递两个参数:target和event,这两个参数都有各自的用途了(target指向事件委托对象,event指向所发生的事件)。看来只依靠Cocoa框架已经无法做到了。

      但我们还是可以利用event参数,在自定义的btnClicked方法中判断出事件发生在UITableView的哪一个cell上。因为UITableView有一个很关键的方法indexPathForRowAtPoint,可以根据触摸发生的位置,返回触摸发生在哪一个cell的indexPath。而且通过event对象,正好也可以获得每个触摸在视图中的位置。

// 检查用户点击按钮时的位置,并转发事件到对应的accessory tapped事件
- (void)btnClicked:(id)sender event:(id)event
{
     NSSet *touches = [event allTouches];
     UITouch *touch = [touches anyObject];
     CGPoint currentTouchPosition = [touch locationInView:self.tableView];
     NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:currentTouchPosition];
     if(indexPath != nil)
     {
         [self tableView:self.tableView accessoryButtonTappedForRowWithIndexPath:indexPath];
     }
}

这样,UITableView的accessoryButtonTappedForRowWithIndexPath方法会被触发,并且获得一个indexPath参数。通过这个indexPath参数,我们即可区分到底哪一行的附件按钮发生了触摸事件。

- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
    int  *idx = indexPath.row;
   //这里加入自己的逻辑
}


来自:http://www.cnblogs.com/lovecode/archive/2012/01/07/2315630.html


    
[2] QT 图片浏览器(2)
    来源: 互联网  发布时间: 2014-02-18
QT 图片浏览器(二)

前边所写的QT图片浏览器程序是在window下QT Creator环境中编写的,在window上面运行效果很正确,但是因为是需要在linux下面的QT环境中编译,所以在linux下面编译以后运行出现的效果却不是理想想过,现象是在打开linux下面的图片以后,上下翻转不按文件夹中已经排列好的顺序显示,而是一个固定的另一个顺序,进而导致删除也不成功,最后更改了遍历打开图片所在文件夹下的全部图片这个功能函数的代码,换了一种遍历方法,最后得到了理想的效果。代码如下:

csimagescan.cpp:

#include "csimagescan.h"
#include "ui_csimagescan.h"

csImageScan::csImageScan(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::csImageScan)
{
    ui->setupUi(this);
    position =0;
    left=0;
    right=0;
    num_Roll = 0;

    setWindowFlags(Qt::FramelessWindowHint);//去掉标题栏
    int screen_W=QApplication::desktop()->width();
    int screen_H=QApplication::desktop()->height();

    resize(screen_W,screen_H);
    //int win_W=width();
    //int win_H=height();
    //
    printf("abcdefghigk=====%d,------12345678910========%d-----------\n\n\n\n",screen_W,screen_H);
    /*
    QPushButton pbexit = new QPushButton(this);
    pbexit->setObjectName(QString::fromUtf8("pbexit"));
    pbexit->setGeometry(QRect(803,728,40,40));
    pbexit->setFocusPolicy(Qt::NoFocus);
    pbexit->setFlat(true);
    */
    ui->pre->setGeometry(523,728,40,40);
    ui->next->setGeometry(603,728,40,40);
    //ui->rotuteleft->setGeometry(203,728,40,40);
    //ui->rotuteright->setGeometry(283,728,40,40);
    ui->pbdelete->setGeometry(723,728,40,40);
    ui->pbexit->setGeometry(QRect(803,728,40,40));
    ui->rotuteleft->setEnabled(false);
    ui->rotuteright->setEnabled(false);

    QPixmap m_pixmap("res/pre.png");
    QIcon m_icon;
    m_icon.addPixmap(m_pixmap);
    ui->pre->setIcon(m_icon);
    ui->pre->setIconSize(QSize(40,40));

    m_pixmap.load("res/next.png");
    m_icon.addPixmap(m_pixmap);
    ui->next->setIcon(m_icon);
    ui->next->setIconSize(QSize(40,40));
/*
    m_pixmap.load("res/left.png");
    m_icon.addPixmap(m_pixmap);
    ui->rotuteleft->setIcon(m_icon);
    ui->rotuteleft->setIconSize(QSize(60,60));

    m_pixmap.load("res/right.png");
    m_icon.addPixmap(m_pixmap);
    ui->rotuteright->setIcon(m_icon);
    ui->rotuteright->setIconSize(QSize(60,60));
*/
    m_pixmap.load("res/imgdelete.png");
    m_icon.addPixmap(m_pixmap);
    ui->pbdelete->setIcon(m_icon);
    ui->pbdelete->setIconSize(QSize(40,40));

    m_pixmap.load("res/exit.png");
    m_icon.addPixmap(m_pixmap);
    ui->pbexit->setIcon(m_icon);
    ui->pbexit->setIconSize(QSize(40,40));
}

csImageScan::~csImageScan()
{
    delete ui;
}


/*
void csImageScan::drawImageOnSrc(QString & str)
{
    //
    ui->label->setPixmap(QPixmap(str));
    //
}
*/

//图片浏览对话框关闭按钮槽
void csImageScan::on_pbexit_clicked()
{
    this->close();
}
//打开图片浏览功能
void csImageScan::on_open_clicked()
{
    position =0;
    stringlist.clear();
    //   printf("%s\n",filepath.toAscii().constData());
    filepath = QFileDialog::getOpenFileName(NULL, QObject::tr("Open Image"), ".",
                                            QObject::tr("Image Files(*.jpg *.png *.bmp)"));

    filelist = filepath.split("/");
    filelist.removeLast ();
    folderpath = filelist.join("/");
    setpixmap(filepath);
    printf("-----%d------\n",position);
}

//遍历打开图片所在文件夹下的全部图片
void csImageScan::Open_File()
{
        QDir dir(folderpath);
        QFileInfoList fileList;
        QFileInfo curFile;
        QStringList filters;
        //filters << "*.jpg"<<"*.png";
        filters << "*.jpg"<<"*.png"<<"*.bmp";
        fileList=dir.entryInfoList(filters,QDir::Dirs|QDir::Files
                     |QDir::Readable|QDir::Writable
                     |QDir::Hidden|QDir::NoDotAndDotDot
                     ,QDir::Name);
        int i=0;
        if(fileList.size()>0)
        {
            //curFile=fileList[position];
            while(i<fileList.size())
            {
                curFile=fileList[i];
                if(filepath == curFile.filePath())
                    position = i;
                stringlist<<curFile.filePath();
                printf("----delete----%d----------\n",i);
                printf("%s----\n",curFile.filePath().toAscii().constData());
                //printf("%s----\n",filepath.toAscii().constData());
                i++;
            }

        }
}

//图片前翻
void csImageScan::on_pre_clicked()
{
    left=0;
    right=0;
    num_Roll = 0;
    if(0 == stringlist.size())
    {
        printf("get pre image erron \n");
        return ;
    }
    position--;
    if(position<0)
    {
        position=stringlist.size()-1;
    }

    filepath = stringlist.at(position);
    setpixmap(filepath);
    printf("---pre----%d-------------------\n",position);
    printf("%s----\n",filepath.toAscii().constData());
}

//图片后翻
void csImageScan::on_next_clicked()
{
    left=0;
    right=0;
    num_Roll = 0;
    if(0 == stringlist.size())
    {
        printf("get next image erron \n");
        return ;
    }
    position++;
    if(position>stringlist.size()-1)
    {
        position=0;
    }
    filepath = stringlist.at(position);
    setpixmap(filepath);
    printf("---next----%d-------------------\n",position);
    printf("%s----\n",filepath.toAscii().constData());
}
//图片逆时针旋转
void csImageScan::on_rotuteleft_clicked()
{
    if(0 == stringlist.size())
    {
        printf("get next image erron \n");
        return ;
    }
    left=1;
    if(left==1&&right==1)
    {
        num_Roll--;
        //QImage image(stringlist.at(position));
        QMatrix matrix;
        //matrix.rotate(-90.0*num_Roll);
        matrix.rotate(-90.0);
        image = image.transformed(matrix,Qt::FastTransformation);
        pix = pix.fromImage(image);
        update();
    }
    else
    {
    num_Roll++;
    //QImage image(stringlist.at(position));
    image.load(stringlist.at(position));
    QMatrix matrix;
    matrix.rotate(-90.0*num_Roll);
    image = image.transformed(matrix,Qt::FastTransformation);
    pix = pix.fromImage(image);
    update();
    }

}
//图片顺时针旋转
void csImageScan::on_rotuteright_clicked()
{
    if(0 == stringlist.size())
    {
        printf("get next image erron \n");
        return ;
    }
    right=1;
    if(left==1&&right==1)
    {
        //num_Roll--;
        //QImage image(stringlist.at(position));
        QMatrix matrix;
        //matrix.rotate(90.0*num_Roll);
        matrix.rotate(90.0);
        image = image.transformed(matrix,Qt::FastTransformation);
        pix = pix.fromImage(image);
        update();
    }
    else
    {
    num_Roll++;
    //QImage image(stringlist.at(position));
    image.load(stringlist.at(position));
    QMatrix matrix;
    matrix.rotate(90.0*num_Roll);
    image = image.transformed(matrix,Qt::FastTransformation);
    pix = pix.fromImage(image);
    update();
    }
}
//图片删除
void csImageScan::on_pbdelete_clicked()
{
    if(stringlist.size() == 0)
        return;
    QMessageBox::StandardButton rb = QMessageBox::question(NULL,"Warning",
                 "Do you want to delete the picture?",QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
    if(rb == QMessageBox::Yes)
    {

        stringlist.takeAt(position);
        QDir dir(folderpath);
        QFileInfoList fileList;
        QFileInfo curFile;
        QStringList filters;
        filters << "*.jpg"<<"*.png"<<"*.bmp";
        fileList=dir.entryInfoList(filters,QDir::Dirs|QDir::Files
                     |QDir::Readable|QDir::Writable
                     |QDir::Hidden|QDir::NoDotAndDotDot
                     ,QDir::Name);
        if(fileList.size()>0)
        {
            curFile=fileList[position];
            QFile fileTemp(curFile.filePath());
            fileTemp.remove();
            fileList.removeAt(position);
            //QFile filetemp(curFile.filePath().remove(QString("_screen"), Qt::CaseSensitive));
            //filetemp.remove();
            printf("----delete----%d----------\n",position);
            printf("%s----\n",curFile.filePath().toAscii().constData());
        }
        if(0 == stringlist.size())
        {
            printf("get next image erron \n");
            return ;
        }
        else
        {
            filepath = stringlist.at(position);
            setpixmap(filepath);
        }
    }
}
//重绘事件
void csImageScan::paintEvent ( QPaintEvent * )
{
    QPainter painter(this);
    painter.drawPixmap(43,0,pix);
 }


void csImageScan::setpixmap(QString imageName)
{
    pix.load(imageName);
    update();
}

其他地方的代码完全相同。


    
[3] H264视频透过RTMP发送
    来源: 互联网  发布时间: 2014-02-18
H264视频通过RTMP发送
       前面的文章中提到了通过RTSP(Real Time Streaming Protocol)的方式来实现视频的直播,但RTSP方式的一个弊端是如果需要支持客户端通过网页来访问,就需要在在页面中嵌入一个ActiveX控件,而ActiveX一般都需要签名才能正常使用,否则用户在使用时还需要更改浏览器设置,并且ActiveX还只支持IE内核的浏览器,Chrome、FireFox需要IE插件才能运行,因此会特别影响用户体验。而RTMP(Real Time Messaging Protocol)很好的解决了这一个问题。由于RTMP是针对FLASH的流媒体协议,视频通过RTMP直播后,只需要在WEB上嵌入一个Web Player(如Jwplayer)即可观看,而且对平台也没什么限制,还可以方便的通过手机观看。

       视频通过RTMP方式发布需要一个RTMP Server(常见的有FMS、Wowza Media Server, 开源的有CRtmpServer、Red5等),原始视频只要按照RTMP协议发送给RTMP Server就可以RTMP视频流的发布了。为了便于视频的打包发布,封装了一个RTMPStream,目前只支持发送H264的视频文件。可以直接发送H264数据帧或H264文件,RTMPStream提供的接口如下。

class CRTMPStream  
{  
public:  
    CRTMPStream(void);  
    ~CRTMPStream(void);  
public:  
    // 连接到RTMP Server  
    bool Connect(const char* url);  
    // 断开连接  
    void Close();  
    // 发送MetaData  
    bool SendMetadata(LPRTMPMetadata lpMetaData);  
    // 发送H264数据帧  
    bool SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp);  
    // 发送H264文件  
    bool SendH264File(const char *pFileName);  
//...  
}  
调用示例:
#include <stdio.h>  
#include "RTMPStream\RTMPStream.h"  
  
int main(int argc,char* argv[])  
{  
    CRTMPStream rtmpSender;  
  
    bool bRet = rtmpSender.Connect("rtmp://192.168.1.104/live/test");  
  
    rtmpSender.SendH264File("E:\\video\\test.264");  
  
    rtmpSender.Close();  
}  

通过JwPlayer播放效果如下:


最后附上RTMPStream完整的代码:

/******************************************************************** 
filename:   RTMPStream.h
created:    2013-04-3
author:     firehood 
purpose:    发送H264视频到RTMP Server,使用libRtmp库
*********************************************************************/ 
#pragma once
#include "rtmp.h"
#include "rtmp_sys.h"
#include "amf.h"
#include <stdio.h>

#define FILEBUFSIZE (1024 * 1024 * 10)       //  10M

// NALU单元
typedef struct _NaluUnit
{
	int type;
	int size;
	unsigned char *data;
}NaluUnit;

typedef struct _RTMPMetadata
{
	// video, must be h264 type
	unsigned int	nWidth;
	unsigned int	nHeight;
	unsigned int	nFrameRate;		// fps
	unsigned int	nVideoDataRate;	// bps
	unsigned int	nSpsLen;
	unsigned char	Sps[1024];
	unsigned int	nPpsLen;
	unsigned char	Pps[1024];

	// audio, must be aac type
	bool	        bHasAudio;
	unsigned int	nAudioSampleRate;
	unsigned int	nAudioSampleSize;
	unsigned int	nAudioChannels;
	char		    pAudioSpecCfg;
	unsigned int	nAudioSpecCfgLen;

} RTMPMetadata,*LPRTMPMetadata;


class CRTMPStream
{
public:
	CRTMPStream(void);
	~CRTMPStream(void);
public:
	// 连接到RTMP Server
	bool Connect(const char* url);
    // 断开连接
	void Close();
    // 发送MetaData
	bool SendMetadata(LPRTMPMetadata lpMetaData);
    // 发送H264数据帧
	bool SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp);
	// 发送H264文件
	bool SendH264File(const char *pFileName);
private:
	// 送缓存中读取一个NALU包
	bool ReadOneNaluFromBuf(NaluUnit &nalu);
	// 发送数据
	int SendPacket(unsigned int nPacketType,unsigned char *data,unsigned int size,unsigned int nTimestamp);
private:
	RTMP* m_pRtmp;
	unsigned char* m_pFileBuf;
	unsigned int  m_nFileBufSize;
	unsigned int  m_nCurPos;
};

/******************************************************************** 
filename:   RTMPStream.cpp
created:    2013-04-3
author:     firehood 
purpose:    发送H264视频到RTMP Server,使用libRtmp库
*********************************************************************/ 
#include "RTMPStream.h"
#include "SpsDecode.h"
#ifdef WIN32  
#include <windows.h>
#endif

enum
{
	FLV_CODECID_H264 = 7,
};

int InitSockets()  
{  
#ifdef WIN32  
	WORD version;  
	WSADATA wsaData;  
	version = MAKEWORD(1, 1);  
	return (WSAStartup(version, &wsaData) == 0);  
#else  
	return TRUE;  
#endif  
}  

inline void CleanupSockets()  
{  
#ifdef WIN32  
	WSACleanup();  
#endif  
}  

char * put_byte( char *output, uint8_t nVal )  
{  
	output[0] = nVal;  
	return output+1;  
}  
char * put_be16(char *output, uint16_t nVal )  
{  
	output[1] = nVal & 0xff;  
	output[0] = nVal >> 8;  
	return output+2;  
}  
char * put_be24(char *output,uint32_t nVal )  
{  
	output[2] = nVal & 0xff;  
	output[1] = nVal >> 8;  
	output[0] = nVal >> 16;  
	return output+3;  
}  
char * put_be32(char *output, uint32_t nVal )  
{  
	output[3] = nVal & 0xff;  
	output[2] = nVal >> 8;  
	output[1] = nVal >> 16;  
	output[0] = nVal >> 24;  
	return output+4;  
}  
char *  put_be64( char *output, uint64_t nVal )  
{  
	output=put_be32( output, nVal >> 32 );  
	output=put_be32( output, nVal );  
	return output;  
}  
char * put_amf_string( char *c, const char *str )  
{  
	uint16_t len = strlen( str );  
	c=put_be16( c, len );  
	memcpy(c,str,len);  
	return c+len;  
}  
char * put_amf_double( char *c, double d )  
{  
	*c++ = AMF_NUMBER;  /* type: Number */  
	{  
		unsigned char *ci, *co;  
		ci = (unsigned char *)&d;  
		co = (unsigned char *)c;  
		co[0] = ci[7];  
		co[1] = ci[6];  
		co[2] = ci[5];  
		co[3] = ci[4];  
		co[4] = ci[3];  
		co[5] = ci[2];  
		co[6] = ci[1];  
		co[7] = ci[0];  
	}  
	return c+8;  
}

CRTMPStream::CRTMPStream(void):
m_pRtmp(NULL),
m_nFileBufSize(0),
m_nCurPos(0)
{
	m_pFileBuf = new unsigned char[FILEBUFSIZE];
	memset(m_pFileBuf,0,FILEBUFSIZE);
	InitSockets();
	m_pRtmp = RTMP_Alloc();  
	RTMP_Init(m_pRtmp);  
}

CRTMPStream::~CRTMPStream(void)
{
	Close();
	WSACleanup();  
	delete[] m_pFileBuf;
}

bool CRTMPStream::Connect(const char* url)
{
	if(RTMP_SetupURL(m_pRtmp, (char*)url)<0)
	{
		return FALSE;
	}
	RTMP_EnableWrite(m_pRtmp);
	if(RTMP_Connect(m_pRtmp, NULL)<0)
	{
		return FALSE;
	}
	if(RTMP_ConnectStream(m_pRtmp,0)<0)
	{
		return FALSE;
	}
	return TRUE;
}

void CRTMPStream::Close()
{
	if(m_pRtmp)
	{
		RTMP_Close(m_pRtmp);
		RTMP_Free(m_pRtmp);
		m_pRtmp = NULL;
	}
}

int CRTMPStream::SendPacket(unsigned int nPacketType,unsigned char *data,unsigned int size,unsigned int nTimestamp)
{
	if(m_pRtmp == NULL)
	{
		return FALSE;
	}

	RTMPPacket packet;
	RTMPPacket_Reset(&packet);
	RTMPPacket_Alloc(&packet,size);

	packet.m_packetType = nPacketType;
	packet.m_nChannel = 0x04;  
	packet.m_headerType = RTMP_PACKET_SIZE_LARGE;  
	packet.m_nTimeStamp = nTimestamp;  
	packet.m_nInfoField2 = m_pRtmp->m_stream_id;
	packet.m_nBodySize = size;
	memcpy(packet.m_body,data,size);

	int nRet = RTMP_SendPacket(m_pRtmp,&packet,0);

	RTMPPacket_Free(&packet);

	return nRet;
}

bool CRTMPStream::SendMetadata(LPRTMPMetadata lpMetaData)
{
	if(lpMetaData == NULL)
	{
		return false;
	}
	char body[1024] = {0};;
    
    char * p = (char *)body;  
	p = put_byte(p, AMF_STRING );
	p = put_amf_string(p , "@setDataFrame" );

	p = put_byte( p, AMF_STRING );
	p = put_amf_string( p, "onMetaData" );

	p = put_byte(p, AMF_OBJECT );  
	p = put_amf_string( p, "copyright" );  
	p = put_byte(p, AMF_STRING );  
	p = put_amf_string( p, "firehood" );  

	p =put_amf_string( p, "width");
	p =put_amf_double( p, lpMetaData->nWidth);

	p =put_amf_string( p, "height");
	p =put_amf_double( p, lpMetaData->nHeight);

	p =put_amf_string( p, "framerate" );
	p =put_amf_double( p, lpMetaData->nFrameRate); 

	p =put_amf_string( p, "videocodecid" );
	p =put_amf_double( p, FLV_CODECID_H264 );

	p =put_amf_string( p, "" );
	p =put_byte( p, AMF_OBJECT_END  );

	int index = p-body;

	SendPacket(RTMP_PACKET_TYPE_INFO,(unsigned char*)body,p-body,0);

	int i = 0;
	body[i++] = 0x17; // 1:keyframe  7:AVC
	body[i++] = 0x00; // AVC sequence header

	body[i++] = 0x00;
	body[i++] = 0x00;
	body[i++] = 0x00; // fill in 0;

	// AVCDecoderConfigurationRecord.
	body[i++] = 0x01; // configurationVersion
	body[i++] = lpMetaData->Sps[1]; // AVCProfileIndication
	body[i++] = lpMetaData->Sps[2]; // profile_compatibility
	body[i++] = lpMetaData->Sps[3]; // AVCLevelIndication 
    body[i++] = 0xff; // lengthSizeMinusOne  

    // sps nums
	body[i++] = 0xE1; //&0x1f
	// sps data length
	body[i++] = lpMetaData->nSpsLen>>8;
	body[i++] = lpMetaData->nSpsLen&0xff;
	// sps data
	memcpy(&body[i],lpMetaData->Sps,lpMetaData->nSpsLen);
	i= i+lpMetaData->nSpsLen;

	// pps nums
	body[i++] = 0x01; //&0x1f
	// pps data length 
	body[i++] = lpMetaData->nPpsLen>>8;
	body[i++] = lpMetaData->nPpsLen&0xff;
	// sps data
	memcpy(&body[i],lpMetaData->Pps,lpMetaData->nPpsLen);
	i= i+lpMetaData->nPpsLen;

	return SendPacket(RTMP_PACKET_TYPE_VIDEO,(unsigned char*)body,i,0);

}

bool CRTMPStream::SendH264Packet(unsigned char *data,unsigned int size,bool bIsKeyFrame,unsigned int nTimeStamp)
{
	if(data == NULL && size<11)
	{
		return false;
	}

	unsigned char *body = new unsigned char[size+9];

	int i = 0;
	if(bIsKeyFrame)
	{
		body[i++] = 0x17;// 1:Iframe  7:AVC
	}
	else
	{
		body[i++] = 0x27;// 2:Pframe  7:AVC
	}
	body[i++] = 0x01;// AVC NALU
	body[i++] = 0x00;
	body[i++] = 0x00;
	body[i++] = 0x00;

	// NALU size
	body[i++] = size>>24;
	body[i++] = size>>16;
	body[i++] = size>>8;
	body[i++] = size&0xff;;

	// NALU data
	memcpy(&body[i],data,size);

	bool bRet = SendPacket(RTMP_PACKET_TYPE_VIDEO,body,i+size,nTimeStamp);

	delete[] body;

	return bRet;
}

bool CRTMPStream::SendH264File(const char *pFileName)
{
	if(pFileName == NULL)
	{
		return FALSE;
	}
	FILE *fp = fopen(pFileName, "rb");  
	if(!fp)  
	{  
		printf("ERROR:open file %s failed!",pFileName);
	}  
	fseek(fp, 0, SEEK_SET);
	m_nFileBufSize = fread(m_pFileBuf, sizeof(unsigned char), FILEBUFSIZE, fp);
	if(m_nFileBufSize >= FILEBUFSIZE)
	{
		printf("warning : File size is larger than BUFSIZE\n");
	}
	fclose(fp);  

	RTMPMetadata metaData;
	memset(&metaData,0,sizeof(RTMPMetadata));

    NaluUnit naluUnit;
	// 读取SPS帧
    ReadOneNaluFromBuf(naluUnit);
	metaData.nSpsLen = naluUnit.size;
	memcpy(metaData.Sps,naluUnit.data,naluUnit.size);

	// 读取PPS帧
	ReadOneNaluFromBuf(naluUnit);
	metaData.nPpsLen = naluUnit.size;
	memcpy(metaData.Pps,naluUnit.data,naluUnit.size);

	// 解码SPS,获取视频图像宽、高信息
	int width = 0,height = 0;
    h264_decode_sps(metaData.Sps,metaData.nSpsLen,width,height);
	metaData.nWidth = width;
    metaData.nHeight = height;
	metaData.nFrameRate = 25;
   
	// 发送MetaData
    SendMetadata(&metaData);

	unsigned int tick = 0;
	while(ReadOneNaluFromBuf(naluUnit))
	{
		bool bKeyframe  = (naluUnit.type == 0x05) ? TRUE : FALSE;
		// 发送H264数据帧
		SendH264Packet(naluUnit.data,naluUnit.size,bKeyframe,tick);
		msleep(40);
		tick +=40;
	}

	return TRUE;
}

bool CRTMPStream::ReadOneNaluFromBuf(NaluUnit &nalu)
{
	int i = m_nCurPos;
	while(i<m_nFileBufSize-4)
	{
		if(m_pFileBuf[i++] == 0x00 &&
			m_pFileBuf[i++] == 0x00 &&
			m_pFileBuf[i++] == 0x00 &&
			m_pFileBuf[i++] == 0x01
			)
		{
			int pos = i;
			while (pos<m_nFileBufSize-4)
			{
				if(m_pFileBuf[pos++] == 0x00 &&
					m_pFileBuf[pos++] == 0x00 &&
					m_pFileBuf[pos++] == 0x00 &&
					m_pFileBuf[pos++] == 0x01
					)
				{
					break;
				}
			}
			nalu.type = m_pFileBuf[i]&0x1f;
			nalu.size = (pos-4)-i;
			nalu.data = &m_pFileBuf[i];

			m_nCurPos = pos-4;
			return TRUE;
		}
	}
	return FALSE;
}




    
最新技术文章:
▪Android开发之登录验证实例教程
▪Android开发之注册登录方法示例
▪Android获取手机SIM卡运营商信息的方法
▪Android实现将已发送的短信写入短信数据库的...
▪Android发送短信功能代码
▪Android根据电话号码获得联系人头像实例代码
▪Android中GPS定位的用法实例
▪Android实现退出时关闭所有Activity的方法
▪Android实现文件的分割和组装
▪Android录音应用实例教程
▪Android双击返回键退出程序的实现方法
▪Android实现侦听电池状态显示、电量及充电动...
▪Android获取当前已连接的wifi信号强度的方法
▪Android实现动态显示或隐藏密码输入框的内容
▪根据USER-AGENT判断手机类型并跳转到相应的app...
▪Android Touch事件分发过程详解
▪Android中实现为TextView添加多个可点击的文本
▪Android程序设计之AIDL实例详解
▪Android显式启动与隐式启动Activity的区别介绍
▪Android按钮单击事件的四种常用写法总结
▪Android消息处理机制Looper和Handler详解
▪Android实现Back功能代码片段总结
▪Android实用的代码片段 常用代码总结
▪Android实现弹出键盘的方法
▪Android中通过view方式获取当前Activity的屏幕截...
▪Android提高之自定义Menu(TabMenu)实现方法
▪Android提高之多方向抽屉实现方法
▪Android提高之MediaPlayer播放网络音频的实现方法...
▪Android提高之MediaPlayer播放网络视频的实现方法...
▪Android提高之手游转电视游戏的模拟操控
 


站内导航:


特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

©2012-2021,,E-mail:www_#163.com(请将#改为@)

浙ICP备11055608号-3