随笔-97  评论-133  文章-4  trackbacks-0
  2017年2月10日
参考以下帖子:
http://blog.csdn.net/vblittleboy/article/details/20121341

作了一下改动,修正了bug,提高适用性:
#include <stdlib.h>
#include 
<stdio.h>
#include 
<string.h>
#include 
<math.h>
 
#include 
<libavutil/opt.h>
#include 
<libavutil/mathematics.h>
#include 
<libavformat/avformat.h>
 
FILE 
* fp_in = NULL;
FILE 
* fp_out = NULL;
 
static 
int frame_count;
 
int main(int argc, char **argv)
{
    
int ret;
    AVCodec 
*audio_codec;
    AVCodecContext 
*c;
    AVFrame 
*frame;
    AVPacket pkt 
= { 0 }; // data and size must be 0;
    
int got_output;
 
    
/* Initialize libavcodec, and register all codecs and formats. */
    av_register_all();
    avcodec_register_all();
    
//avdevice_register_all();
 
    audio_codec 
= avcodec_find_encoder(AV_CODEC_ID_AAC);
    c 
= avcodec_alloc_context3(audio_codec);
//    c->strict_std_compliance =FF_COMPLIANCE_EXPERIMENTAL;   
    c
->codec_id = AV_CODEC_ID_AAC;
    c
->sample_fmt = AV_SAMPLE_FMT_S16;
    c
->sample_rate = 44100;
    c
->channels = 2;
    c
->channel_layout = AV_CH_LAYOUT_STEREO;
    c
->bit_rate = 64000;
 
    
/* open the codec */
    ret 
= avcodec_open2(c, audio_codec, NULL);
    
if (ret < 0) {
        fprintf(stderr, 
"Could not open video codec: %s\n", av_err2str(ret));
        
exit(1);
    }
 
    
/* allocate and init a re-usable frame */
#
if 0
    frame 
= avcodec_alloc_frame();
#
else
    frame 
= av_frame_alloc();
#endif
    
if (!frame) {
        fprintf(stderr, 
"Could not allocate video frame\n");
        
exit(1);
    }
 
 
    frame
->nb_samples = c->frame_size;
    frame
->format = c->sample_fmt;
    frame
->channels = c->channels;
    frame
->channel_layout = c->channel_layout;
#
if 0
    frame
->linesize[0= 4096;
    frame
->extended_data = frame->data[0= av_malloc((size_t)frame->linesize[0]);
#
else
    ret 
= av_frame_get_buffer(frame, 0);
    
if (ret < 0) {
        fprintf(stderr, 
"Could not allocate an audio frame.\n");
        
exit(1);
    }
    printf(
"----nb_samples= %d, linesize= %d\n", frame->nb_samples, frame->linesize[0]);
#endif

    av_init_packet(
&pkt);

    fp_in 
= fopen("in.wav","rb");
    fp_out
= fopen("out.aac","wb");
 
    
//printf("frame->nb_samples = %d\n",frame->nb_samples);
     
    
while(1)
    {
        frame_count
++;
        bzero(frame
->data[0],frame->linesize[0]);
        ret 
= fread(frame->data[0],frame->linesize[0],1,fp_in);
        
if(ret <= 0)
        {
            printf(
"read over !\n");
            break;
        }
        ret 
= avcodec_encode_audio2(c, &pkt, frame, &got_output);
        
if (ret < 0) {
            fprintf(stderr, 
"Error encoding audio frame: %s\n", av_err2str(ret));
            
exit(1);
        }
     
        
if(got_output > 0)
        {
            
//printf("pkt.size = %d\n",pkt.size);
            fwrite(pkt.data,pkt.size,
1,fp_out);
            av_free_packet(
&pkt);
        }
 
        #
if 0
        
if(frame_count > 10)
        {
            printf(
"break @@@@@@@@@@@@\n");
            break;
        }
        #endif
    }
 
    avcodec_close(c);
    av_free(c);
#
if 0
    avcodec_free_frame(
&frame);
#
else
    av_frame_free(
&frame);
#endif
    fclose(fp_in);
    fclose(fp_out);
 
    return 
0;
}

另外,建议看一下ffmpeg自带的例程,很有参考价值(特别是需要用到重采样功能):
doc/examples/transcode_aac.c

注:
    aac编码用到了libfdk_aac库,详细请参考:
http://trac.ffmpeg.org/wiki/Encode/AAC
posted @ 2017-02-10 11:24 lfc 阅读(6) | 评论 (0)编辑 收藏
  2017年2月8日
基础搞明白了,那么live555的RTSP服务器,又是如何创建、启动,如何和Source和Sink建立联系的呢?

主程序中会调用类似下面的代码,创建RTSP服务器:
  // Create the RTSP server:
  RTSPServer
* rtspServer = RTSPServer::createNew(*env, 554, authDB);
  
if (rtspServer == NULL) {
    
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
    
exit(1);
  }

父类GenericMediaServer被构建,后续承担基础的服务:
RTSPServer::RTSPServer(UsageEnvironment& env,
               
int ourSocket, Port ourPort,
               UserAuthenticationDatabase
* authDatabase,
               unsigned reclamationSeconds)
  : GenericMediaServer(env, ourSocket, ourPort, reclamationSeconds),
    fHTTPServerSocket(
-1), fHTTPServerPort(0),
    fClientConnectionsForHTTPTunneling(
NULL), // will get created if needed
    fTCPStreamingDatabase(HashTable::create(ONE_WORD_HASH_KEYS)),
    fPendingRegisterOrDeregisterRequests(HashTable::create(ONE_WORD_HASH_KEYS)),
    fRegisterOrDeregisterRequestCounter(
0), fAuthDB(authDatabase), fAllowStreamingRTPOverTCP(True) {
}

GenericMediaServer会创建一个后台任务,由于监听Client的连接:
GenericMediaServer
::GenericMediaServer(UsageEnvironment
& env, int ourSocket, Port ourPort,
             unsigned reclamationSeconds)
  : Medium(env),
    fServerSocket(ourSocket), fServerPort(ourPort), fReclamationSeconds(reclamationSeconds),
    fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)),
    fClientConnections(HashTable::create(ONE_WORD_HASH_KEYS)),
    fClientSessions(HashTable::create(STRING_HASH_KEYS)) {
  ignoreSigPipeOnSocket(fServerSocket); 
// so that clients on the same host that are killed don't also kill us

  
// Arrange to handle connections from others:
  env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket, 
incomingConnectionHandler, this); //创建后台任务用于监听client的connect
}

收到Client的连接请求后,incomingConnectionHandler函数会被调用。

void GenericMediaServer::incomingConnectionHandlerOnSocket(int serverSocket) {
  struct sockaddr_in clientAddr;
  SOCKLEN_T clientAddrLen 
= sizeof clientAddr;
  
int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
  
if (clientSocket < 0) {
    
int err = envir().getErrno();
    
if (err != EWOULDBLOCK) {
      envir().setResultErrMsg(
"accept() failed: ");
    }
    return;
  }

  
// Create a new object for handling this connection:
  (void)createNewClientConnection(clientSocket, clientAddr);
}

live555会为每个连接调用createNewClientConnection创建一个ClientConnection

RTSPServer::RTSPClientConnection
::RTSPClientConnection(RTSPServer
& ourServer, int clientSocket, struct sockaddr_in clientAddr)
  : 
GenericMediaServer::ClientConnection
    
fIsActive((ourServer, clientSocket, clientAddr),
    fOurRTSPServer(ourServer), fClientInputSocket(fOurSocket), fClientOutputSocket(fOurSocket),
True), fRecursionCount(0), fOurSessionCookie(NULL) {
  resetRequestBuffer();
}

GenericMediaServer::ClientConnection会创建一个后台任务,由于监听connect上的Request:
GenericMediaServer::ClientConnection
::ClientConnection(GenericMediaServer
& ourServer, int clientSocket, struct sockaddr_in clientAddr)
  : fOurServer(ourServer), fOurSocket(clientSocket), fClientAddr(clientAddr) {
  
// Add ourself to our 'client connections' table:
  fOurServer.fClientConnections->Add((char const*)this, this);

  
// Arrange to handle incoming requests:
  resetRequestBuffer();
  envir().taskScheduler()
    .setBackgroundHandling(fOurSocket, SOCKET_READABLE|SOCKET_EXCEPTION, 
incomingRequestHandler, this); //创建后台任务用于监听connect上的Request
}

当Connect上收到来自Client的请求后,incomingRequestHandler函数会被调用。

void GenericMediaServer::ClientConnection::incomingRequestHandler() {
  struct sockaddr_in dummy; 
// 'from' address, meaningless in this case

  
int bytesRead = readSocket(envir(), fOurSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy);
  handleRequestBytes(bytesRead);
}

void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead) {
.
    
Boolean parseSucceeded = parseRTSPRequestString((char*)fRequestBuffer, fLastCRLF+2 - fRequestBuffer,    
                            cmdName, sizeof cmdName,
                            urlPreSuffix, sizeof urlPreSuffix,
                            urlSuffix, sizeof urlSuffix,
                            cseq, sizeof cseq,
                            sessionIdStr, sizeof sessionIdStr,
                            contentLength);

.
      } 
else if (strcmp(cmdName, "DESCRIBE"== 0) {    //处理DESCRIBE request
        handleCmd_DESCRIBE(urlPreSuffix, urlSuffix, (char 
const*)fRequestBuffer);
      } 
else if (strcmp(cmdName, "SETUP"== 0) {   //处理SETUP request
            clientSession 
= (RTSPServer::RTSPClientSession*)fOurRTSPServer.createNewClientSessionWithId();
      }
        
if (clientSession != NULL) {
          clientSession
->handleCmd_SETUP(this, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
        }
      } 
else if (strcmp(cmdName, "TEARDOWN"== 0
         || strcmp(cmdName, 
"PLAY"== 0
         || strcmp(cmdName, 
"PAUSE"== 0
         || strcmp(cmdName, 
"GET_PARAMETER"== 0
         || strcmp(cmdName, 
"SET_PARAMETER"== 0) {
        
if (clientSession != NULL) {
          clientSession
->handleCmd_withinSession(this, cmdName, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);  //处理PLAY请求等

        }
.
}

具体RTSP协议下的Request和Respond不作详细介绍,这里只关注流程部分:
1、先看handleCmd_SETUP
void RTSPServer::RTSPClientSession
::handleCmd_SETUP(RTSPServer::RTSPClientConnection
* ourClientConnection,
          char 
const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr) {

fstreamStates = new struct streamState[fNumStreamStates];  //每个会话的状态,用streamState来管理
....
    subsession
->getStreamParameters(fOurSessionId, ourClientConnection->fClientAddr.sin_addr.s_addr,
                    clientRTPPort, clientRTCPPort,
                    fStreamStates[trackNum].tcpSocketNum, rtpChannelId, rtcpChannelId,
                    destinationAddress, destinationTTL, fIsMulticast,
                    serverRTPPort, serverRTCPPort,
                    fStreamStates[trackNum].streamToken);
.
}

getStreamParmeters这个函数很重要,它将完成source,RTPSink的创建工作,并将其与客户端建立联系
void OnDemandServerMediaSubsession
::getStreamParameters(unsigned clientSessionId,
              netAddressBits clientAddress,
              Port 
const& clientRTPPort,
              Port 
const& clientRTCPPort,
              
int tcpSocketNum,
              unsigned char rtpChannelId,
              unsigned char rtcpChannelId,
              netAddressBits
& destinationAddress,
              u_int8_t
& /*destinationTTL*/,
              
Boolean& isMulticast,
              Port
& serverRTPPort,
              Port
& serverRTCPPort,
              void
*& streamToken) {
.
    FramedSource* mediaSource
      
= createNewStreamSource(clientSessionId, streamBitrate);

.
    rtpSink = createNewRTPSink(rtpGroupsock, rtpPayloadType, mediaSource);

.
    streamToken = fLastStreamToken
      
= new StreamState(*this, serverRTPPort, serverRTCPPort, rtpSink, udpSink, 
            streamBitrate, mediaSource,
            rtpGroupsock, rtcpGroupsock);}

.

2、再来看handleCmd_PLAY
void RTSPServer::RTSPClientSession
::handleCmd_PLAY(RTSPServer::RTSPClientConnection
* ourClientConnection,
         ServerMediaSubsession
* subsession, char const* fullRequestStr) {
.
  
// Now, start streaming:
  
for (i = 0; i < fNumStreamStates; ++i) {
    
if (subsession == NULL /* means: aggregated operation */
    || subsession 
== fStreamStates[i].subsession) {
      unsigned short rtpSeqNum 
= 0;
      unsigned rtpTimestamp 
= 0;
      
if (fStreamStates[i].subsession == NULL) continue;
      fStreamStates[i].subsession
->startStream(fOurSessionId, 
                           fStreamStates[i].streamToken,
                           (TaskFunc
*)noteClientLiveness, this,
                           rtpSeqNum, rtpTimestamp,
                           RTSPServer::RTSPClientConnection::handleAlternativeRequestByte, ourClientConnection);
.
}

void OnDemandServerMediaSubsession::startStream(unsigned clientSessionId,
                        void
* streamToken,
                        TaskFunc
* rtcpRRHandler,
                        void
* rtcpRRHandlerClientData,
                        unsigned short
& rtpSeqNum,
                        unsigned
& rtpTimestamp,
                        ServerRequestAlternativeByteHandler
* serverRequestAlternativeByteHandler,
                        void
* serverRequestAlternativeByteHandlerClientData) {
.
  
if (streamState != NULL) {
    streamState
->startPlaying(destinations, clientSessionId,   
                  rtcpRRHandler, rtcpRRHandlerClientData,
                  serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData);
.
}

void StreamState
::startPlaying(Destinations
* dests, unsigned clientSessionId,
           TaskFunc
* rtcpRRHandler, void* rtcpRRHandlerClientData,
           ServerRequestAlternativeByteHandler
* serverRequestAlternativeByteHandler,
           void
* serverRequestAlternativeByteHandlerClientData) {
.
  
if (!fAreCurrentlyPlaying && fMediaSource != NULL) {
    
if (fRTPSink != NULL) {
      fRTPSink
->startPlaying(*fMediaSource, afterPlayingStreamState, this);
      fAreCurrentlyPlaying 
= True;
.
}

Boolean MediaSink::startPlaying(MediaSource& source,
                afterPlayingFunc
* afterFunc,
                void
* afterClientData) {
.
return continuePlaying();
}

Boolean H264or5VideoRTPSink::continuePlaying() {
  
// First, check whether we have a 'fragmenter' class set up yet.
  // If not, create it now:
  
if (fOurFragmenter == NULL) {
    fOurFragmenter 
= new H264or5Fragmenter(fHNumber, envir(), fSource, OutPacketBuffer::maxSize,   
                       ourMaxPacketSize() 
- 12/*RTP hdr size*/);
  } 
else {
    fOurFragmenter
->reassignInputSource(fSource);
  }
  fSource 
= fOurFragmenter;

  
// Then call the parent class's implementation:
  return MultiFramedRTPSink::continuePlaying();
}

Boolean MultiFramedRTPSink::continuePlaying() {
  
// Send the first packet.
  
// (This will also schedule any future sends.)
  buildAndSendPacket(
True);
  return 
True;
}

绕了好大一个圈,终于到达MultiFrameRTPSink的continuePlaying了,从现在开始,它将循环的获取RTSP服务器需要的RTP数据包,直到收到停止命令。
posted @ 2017-02-08 16:47 lfc 阅读(6) | 评论 (0)编辑 收藏
  2017年1月20日
以下只作为个人总结,只作记录用,如果想系统的分析live555,建议阅读以下帖子,或阅读源码:
http://blog.csdn.net/niu_gao/article/details/6906055

一、概念
live555类似于Gstreamer和DirectShow架构,分Source、Filter、Sink的概念,例如,测试程序testOnDemandRTSPServer中,流化H264的pipeline如下(通过H264VideoFileServerMediaSubsession自动构建):

【Source】
ByteStreamFileSource->H264or5VideoStreamParser(MPEGVideoStreamParser)->H264VideoStreamFramer(H264or5VideoStreamFramer)

直接与Sink打交道的是H264VideoStreamFramer(通过H264VideoFileServerMediaSubsession的createNewStreamSource),其它Parser、FileSource是H264VideoStreamFramer自动构建(按需要)

【Sink】
H264or5Fragmenter->H264VideoRTPSink(H264or5VideoRTPSink)->VideoRTPSink->MultiFramedRTPSink

H264or5Fragmenter与上面的H264or5VideoStreamFramer打交道,获取Source的Frame数据后分段成RTP包输出。

二、数据流动
1、先来看数据的输入
1)首先,Sink下会创建缓冲,用于存放从Source获取的数据,存放缓冲的指针就是大家比较熟悉的fTo,Sink通过一系列的类,把指针传递下去,具体是:
H264or5Fragmenter->MPEGVideoStreamFramer->MPEGVideoStreamParser

最终从Sink传递到Parser中,相关的代码片段是:
H264or5Fragmenter::H264or5Fragmenter
{
fInputBuffer 
= new unsignedchar[fInputBufferSize];
}

void H264or5Fragmenter::doGetNextFrame()
{
fInputSource
->getNextFrame(&fInputBuffer[1], fInputBufferSize - 1,  
afterGettingFrame, this,
FramedSource::handleClosure, this);
}

MPEGVideoStreamFramer::doGetNextFrame()
{
  fParser
->registerReadInterest(fTo, fMaxSize);
  continueReadProcessing();
}

void MPEGVideoStreamParser::registerReadInterest(unsigned char
* to,
                         unsigned maxSize) {
  fStartOfFrame 
= fTo = fSavedTo = to
  fLimit 
= to + maxSize;
  fNumTruncatedBytes 
= fSavedNumTruncatedBytes = 0;
}

2)或许你注意到,fTo还没传递到最终的Source(ByteStreamFileSource),那是因为ByteStreamFileSource是由Parser来访问的,而Parser本身会建立Buffer用于存储从ByteStreamFileSource读取的数据,再把分析出来的NAL写入fTo(来自Sink),所以你就可以理解为什么fTo只到达了Parser,而不到达ByteStreamFileSource了吧。

相关代码如下:
StreamParser::StreamParser
{
  fBank[
0= new unsigned char[BANK_SIZE];
  fBank[
1= new unsigned char[BANK_SIZE];
}

StreamParser::ensureValidBytes1
{
  unsigned maxNumBytesToRead 
= BANK_SIZE - fTotNumValidBytes; 
  fInputSource
->getNextFrame(&curBank()[fTotNumValidBytes], 
                 maxNumBytesToRead,
                 afterGettingBytes, this,
                 onInputClosure, this);
}

unsigned H264or5VideoStreamParser::parse()
{
saveXBytes(Y);
}

class MPEGVideoStreamParser: 
public StreamParser 
{
  
// Record "byte" in the current output frame:
  void saveByte(u_int8_t 
byte) {
    
if (fTo >= fLimit) { // there's no space left
      ++fNumTruncatedBytes;
      return;
    }

    
*fTo++ = byte;
  }
}

2、再来看数据的输出
1)上面输入的数据最终去到H264or5Fragmenter,这里说明一下:
H264or5Fragmenter还是FrameSource(在H264or5VideoRTSPSink.cpp内定义),用于连接H264VideoRTSPSink和H264VideoStreamFramer;H264or5Fragmenter的doGetNextFrame实现,会把从Source获取到的数据,按照RTP协议的要求进行分段,保存到Sink的fOutBuf中。

具体代码如下:
MultiFramedRTPSink::MultiFramedRTPSink(UsageEnvironment& env,
                       Groupsock
* rtpGS,
                       unsigned char rtpPayloadType,
                       unsigned rtpTimestampFrequency,
                       char 
const* rtpPayloadFormatName,
                       unsigned numChannels)
  : RTPSink(env, rtpGS, rtpPayloadType, rtpTimestampFrequency,
        rtpPayloadFormatName, numChannels),
    fOutBuf(
NULL), fCurFragmentationOffset(0), fPreviousFrameEndedFragmentation(False),
    fOnSendErrorFunc(
NULL), fOnSendErrorData(NULL) {
  setPacketSizes((RTP_PAYLOAD_PREFERRED_SIZE), (RTP_PAYLOAD_MAX_SIZE)); 
//sihid
}

void MultiFramedRTPSink::setPacketSizes(unsigned preferredPacketSize,
                    unsigned maxPacketSize) {
  
if (preferredPacketSize > maxPacketSize || preferredPacketSize == 0) return;
      
// sanity check

  delete fOutBuf;
  fOutBuf 
= new OutPacketBuffer(preferredPacketSize, maxPacketSize);
  fOurMaxPacketSize 
= maxPacketSize; // save value, in case subclasses need it
}

Boolean MultiFramedRTPSink::continuePlaying() {
  
// Send the first packet.
  
// (This will also schedule any future sends.)
  buildAndSendPacket(
True);
  return 
True;
}

void MultiFramedRTPSink::buildAndSendPacket(
Boolean isFirstPacket) {
..
  packFrame();
}

void MultiFramedRTPSink::packFrame() {
  
// Get the next frame.
..
    
// Normal case: we need to read a new frame from the source
    
if (fSource == NULL) return;
    fSource
->getNextFrame(fOutBuf->curPtr(), fOutBuf->totalBytesAvailable(),
              afterGettingFrame, this, ourHandleClosure, this);
}
void H264or5Fragmenter::doGetNextFrame() {
  
if (fNumValidDataBytes == 1) {
    
// We have no NAL unit data currently in the buffer.  Read a new one:
    fInputSource
->getNextFrame(&fInputBuffer[1], fInputBufferSize - 1,  //Sink调用Source的getNextFrame获取数据
                   afterGettingFrame, this,
                   FramedSource::handleClosure, this);
  } 
else {
..
memmove(fTo, 
&fInputBuffer[1], fNumValidDataBytes - 1);
..
memmove(fTo, fInputBuffer, fMaxSize);
..
memmove(fTo, 
&fInputBuffer[fCurDataOffset-numExtraHeaderBytes], numBytesToSend);
..
}

看到了吧,数据的输入操作,其实是由Sink(MultiFramedRTPSink)发起的,当Sink需要获取数据时,通过调用Source的getNextFrame操作(具体由Source的doGetNextFrame操作来实现),经过一系列的类操作(Source->Filter->Sink),获取到Sink想要的数据。

2)到目前为止,终于可以构建出完整的Pipeline了:ByteStreamFileSource->H264or5VideoStreamParser(MPEGVideoStreamParser)->H264VideoStreamFramer(H264or5VideoStreamFramer)->H264or5Fragmenter->H264VideoRTPSink(H264or5VideoRTPSink)->VideoRTPSink->MultiFramedRTPSink

三、设计思想

1、对于Buffer
上面的Pipeline中,有几处Buffer,对于实时性要求比较高的应用,有必要理清buffer的数量和控制buffer的大小
1)StreamParser会产生buffer,大小是BANK_SIZE(150000),那是因为StreamParser的前端是无格式的ByteStream,后面是完整的一帧数据(NAL),需要通过Parser来处理;
2)H264or5Fragmenter会产生buffer,用于StreamParser存放分析之后的数据(NAL),并生成后端RTPSink需要的RTP包;
3)MultiFramedRTPSink会产生buffer,用于Fragmenter存放分段之后的数据(RTP),以供RTSP服务器使用;

2、对于fTo
fTo,顾名思义,就是To的buffer指针,也就是后端提供的buffer指针;那fTo是在什么时候赋值的呢,答案在这里:

void FramedSource::getNextFrame(unsigned char* to, unsigned maxSize,
                afterGettingFunc
* afterGettingFunc,
                void
* afterGettingClientData,
                onCloseFunc
* onCloseFunc,
                void
* onCloseClientData) {
  
// Make sure we're not already being read:
  if (fIsCurrentlyAwaitingData) {
    envir() 
<< "FramedSource[" << this << "]::getNextFrame(): attempting to read more than once at the same time!\n";
    envir().internalError();
  }

  fTo 
= to//把Sink的bufer赋给Source的fTo sihid
  fMaxSize 
= maxSize; //设置FrameSource的MaxSize sihid
  fNumTruncatedBytes 
= 0// by default; could be changed by doGetNextFrame()
  fDurationInMicroseconds 
= 0// by default; could be changed by doGetNextFrame()
  fAfterGettingFunc 
= afterGettingFunc;
  fAfterGettingClientData 
= afterGettingClientData;
  fOnCloseFunc 
= onCloseFunc;
  fOnCloseClientData 
= onCloseClientData;
  fIsCurrentlyAwaitingData 
= True;

  doGetNextFrame();
}

另外,MPEGVideoStreamFramer还会通过其它方式传递fTo给MPEGVideoStreamParser:

MPEGVideoStreamFramer::doGetNextFrame()
{
  fParser->registerReadInterest(fTo, fMaxSize);
  continueReadProcessing();
}


void MPEGVideoStreamParser::registerReadInterest(unsigned char* to,unsigned maxSize) {
  fStartOfFrame = fTo = fSavedTo = to;
  fLimit = to + maxSize;
  fNumTruncatedBytes = fSavedNumTruncatedBytes = 0;
}

原因是MPEGVideoStreamParser(StreamParser)不是FrameSource,所以只能提供另外的API来获取fTo
当Sink需要数据时,会调用Source的getNextFrame方法,同时把自身的buffer通过参数(to)传递给Source,保存在Source的fTo中。

3、对于fSource
Boolean MediaSink::startPlaying(MediaSource& source,
                afterPlayingFunc
* afterFunc,
                void
* afterClientData) {
  
// Make sure we're not already being played:
  if (fSource != NULL) {
    envir().setResultMsg(
"This sink is already being played");
    return 
False;
  }

  
// Make sure our source is compatible:
  
if (!sourceIsCompatibleWithUs(source)) {
    envir().setResultMsg(
"MediaSink::startPlaying(): source is not compatible!");
    return 
False;
  }
  fSource 
= (FramedSource*)&source; 

  fAfterFunc 
= afterFunc;
  fAfterClientData 
= afterClientData;
  return continuePlaying();
}

Sink的fSource,在startPlaying中被设置为createNewStreamSource返回的H264VideoStreamFramer。

随后在continuePlaying(H264or5VideoRTPSink实现)方法中被修改为H264or5Fragmenter,同时通过reassignInputSource函数记录H264VideoStreamFramer为fInputSource,这样就把fSource和fInputSource区分出来。

Boolean H264or5VideoRTPSink::continuePlaying() {
  
// First, check whether we have a 'fragmenter' class set up yet.
  // If not, create it now:
  
if (fOurFragmenter == NULL) {
    fOurFragmenter 
= new H264or5Fragmenter(fHNumber, envir(), fSource, OutPacketBuffer::maxSize, ourMaxPacketSize() - 12/*RTP hdr size*/);
  } 
else {
    fOurFragmenter
->reassignInputSource(fSource); 
  }
  fSource 
= fOurFragmenter; 

  
// Then call the parent class's implementation:  return MultiFramedRTPSink::continuePlaying();
}

class FramedFilter: public FramedSource {
public:
  FramedSource* inputSource() const { return fInputSource; }
  void reassignInputSource(FramedSource* newInputSource) { fInputSource = newInputSource; }
  // Call before destruction if you want to prevent the destructor from closing the input source
  void detachInputSource();
protected:
  FramedFilter(UsageEnvironment& env, FramedSource* inputSource);
     // abstract base class
  virtual ~FramedFilter();
protected:
  // Redefined virtual functions (with default 'null' implementations):
  virtual char const* MIMEtype() const;
  virtual void getAttributes() const;
  virtual void doStopGettingFrames();
protected:
  FramedSource* fInputSource; //输入文件对应的Source
};

为什么要这么做?因为RTPSink需要的数据,需要在H264VideoStreamFramer输出数据的基础上,通过H264or5Fragmenter封装成RTP包,所以多出了H264or5Fragmenter这个东西(类似于前面的H264or5VideoStreamParser)。

4、对于fInputSource

class FramedFilter下定义,被H264or5VideoStreamFramer和H264or5Fragmenter所继承。可是,fInputSource在这两个类下,指向的内容是不同的。
1)H264or5VideoStreamFramer下指向ByteStreamFileSource,具体见以下代码:
FramedSource* H264VideoFileServerMediaSubsession::createNewStreamSource(unsigned /*clientSessionId*/, unsigned& estBitrate) {
  estBitrate 
= 500// kbps, estimate

  
// Create the video source:
  
ByteStreamFileSource* fileSource = ByteStreamFileSource::createNew(envir(), fFileName);
  
if (fileSource == NULL) return NULL;
  fFileSize 
= fileSource->fileSize();

  
// Create a framer for the Video Elementary Stream:
  return H264VideoStreamFramer::createNew(envir(), 
fileSource); //把ByteStreamFileSource记录到H264VideoStreamFramer的fInputSource

}

H264or5VideoStreamFramer
::H264or5VideoStreamFramer(
int hNumber, UsageEnvironment& env, FramedSource* inputSource,
               
Boolean createParser, Boolean includeStartCodeInOutput)
  : MPEGVideoStreamFramer(env, inputSource),
    fHNumber(hNumber),
    fLastSeenVPS(
NULL), fLastSeenVPSSize(0),
    fLastSeenSPS(
NULL), fLastSeenSPSSize(0),
    fLastSeenPPS(
NULL), fLastSeenPPSSize(0) {
.
}

MPEGVideoStreamFramer::MPEGVideoStreamFramer(UsageEnvironment& env,
                         FramedSource
* inputSource)
  : FramedFilter(env, inputSource),
    fFrameRate(
0.0/* until we learn otherwise */,
    fParser(
NULL) {
  reset();
}

2)H264or5Fragmenter下指向H264VideoStreamFramer,具体见以下代码:
Boolean H264or5VideoRTPSink::continuePlaying() {
  
// First, check whether we have a 'fragmenter' class set up yet.
  // If not, create it now:
  
if (fOurFragmenter == NULL) {
    fOurFragmenter 
= new H264or5Fragmenter(fHNumber, envir(), fSource, OutPacketBuffer::maxSize,    //OutPacketBuffer::maxSize决定了fInputBufferSize. sihid
                       ourMaxPacketSize() 
- 12/*RTP hdr size*/);
  } 
else {
    fOurFragmenter
->reassignInputSource(fSource); //把fSource(对应H264VideoStreamFramer)保存到fOurFragmenter(对应H264or5Fragmenter)的fInputSource
  }
  fSource 
= fOurFragmenter;

  
// Then call the parent class's implementation:
  return MultiFramedRTPSink::continuePlaying();
}
posted @ 2017-01-20 12:15 lfc 阅读(21) | 评论 (0)编辑 收藏
  2016年7月14日
【libxml2-2.9.2】

./configure --host=arm-linux-gnueabi --prefix=/home/luofc/work/tools/gcc-linaro-arm-linux-gnueabi-4.6.3-2012.02-20120201_linux/arm-linux-gnueabi --with-python=/home/luofc/work/tools/libxml2-2.9.2/python
make;make install

【Python-2.7.3】

patch -p1 < ../Python-2.7.3-xcompile.patch
./configure --host=arm-linux-gnueabi --prefix=/home/luofc/python
make HOSTPYTHON=./hostpython HOSTPGEN=./Parser/hostpgen BLDSHARED="arm-linux-gnueabi-gcc -shared" CROSS_COMPILE=arm-linux-gnueabi- CROSS_COMPILE_TARGET=yes
make;make install

【libplist】
PKG_CONFIG_PATH=/home/luofc/work/tools/gcc-linaro-arm-linux-gnueabi-4.6.3-2012.02-20120201_linux/arm-linux-gnueabi/lib/pkgconfig ./configure --host=arm-linux-gnueabi --prefix=/home/luofc/libimobiledevice/libplist LDFLAGS="-L/home/luofc/python/lib"
make;make install

【libusbmuxd】
PKG_CONFIG_PATH=/home/luofc/work/tools/gcc-linaro-arm-linux-gnueabi-4.6.3-2012.02-20120201_linux/arm-linux-gnueabi/lib/pkgconfig:/home/luofc/libimobiledevice/libplist/lib/pkgconfig ./configure --host=arm-linux-gnueabi --prefix=/home/luofc/libimobiledevice/libusbmuxd
make;make install

【usbmuxd】
PKG_CONFIG_PATH=/home/luofc/work/tools/gcc-linaro-arm-linux-gnueabi-4.6.3-2012.02-20120201_linux/arm-linux-gnueabi/lib/pkgconfig:/home/luofc/libimobiledevice/libplist/lib/pkgconfig ./configure --host=arm-linux-gnueabi --prefix=/home/luofc/libimobiledevice/usbmuxd --without-preflight
make;make install
posted @ 2016-07-14 10:07 lfc 阅读(63) | 评论 (0)编辑 收藏
  2016年6月23日
1.host-m4-1.4.15

In file includedfrom clean-temp.h:22:0,

from clean-temp.c:23:

./stdio.h:456:1:error: 'gets' undeclared here (not in a function)

_GL_WARN_ON_USE(gets, "gets is a security hole - use fgets instead");

解决方法:

参考链接:

http://www.civilnet.cn/talk/browse.php?topicno=78555,2楼.

找到:host-m4-1.4.15/lib/stdio.h,然后对stdio.h文件做出如下改动,必要时连同stdio.in.h一起修改:

[plain] view plain copy 在CODE上查看代码片派生到我的代码片
  1. <span style="font-family:Arial;font-size:12px;"># Begin patch  
  2. === modified file 'grub-core/gnulib/stdio.in.h'  
  3. --- grub-core/gnulib/stdio.in.h 2010-09-20 10:35:33 +0000  
  4. +++ grub-core/gnulib/stdio.in.h 2012-07-04 15:18:15 +0000  
  5. @@ -140,8 +140,10 @@  
  6.  /* It is very rare that the developer ever has full control of stdin,  
  7.     so any use of gets warrants an unconditional warning.  Assume it is  
  8.     always declared, since it is required by C89.  */  
  9. +#if defined gets  
  10.  #undef gets  
  11.  _GL_WARN_ON_USE (gets, "gets is a security hole - use fgets instead");  
  12. +#endif  



2.host-autoconf-2.65

conftest.c:14625:must be after `@defmac' to use `@defmacx'

make[3]: ***[autoconf.info] Error 1

make[3]: Leavingdirectory`//opt/Android/a23androidSRC/lichee/out/linux/common/buildroot/build/host-autoconf-2.65/doc'

make[2]: ***[install-recursive] Error 1

make[2]: Leavingdirectory`/opt/Android/a23androidSRC/lichee/out/linux/common/buildroot/build/host-autoconf-2.65'

make[1]: ***[install] Error 2

make[1]: Leavingdirectory`/opt/Android/a23androidSRC/lichee/out/linux/common/buildroot/build/host-autoconf-2.65'

make: ***[/opt/Android/a23androidSRC/lichee/out/linux/common/buildroot/build/host-autoconf-2.65/.stamp_host_installed]Error 2

解决方法如下:

参考链接:

http://gnu-autoconf.7623.n7.nabble.com/compile-error-conftest-c-14625-must-be-after-defmac-to-use-defmacx-td18843.html

2楼有个补丁文件:

[plain] view plain copy 在CODE上查看代码片派生到我的代码片
  1. --- autoconf-2.65/doc/autoconf.texi 2009-11-05 10:42:15.000000000 +0800  
  2. +++ autoconf-2.65/doc/autoconf.texi.new 2013-05-28 05:41:09.243770263 +0800  
  3. @@ -15,7 +15,7 @@  
  4.  @c The ARG is an optional argument.  To be used for macro arguments in  
  5.  @c their documentation (@defmac).  
  6.  @macro ovar{varname}  
  7. -@r{[}@var{\varname\}@r{]}@c  
  8. +@r{[}@var{\varname\}@r{]}  
  9.  @end macro  
  10.    
  11.  @c @dvar(ARG, DEFAULT)  
  12. @@ -23,7 +23,7 @@  
  13.  @c The ARG is an optional argument, defaulting to DEFAULT.  To be used  
  14.  @c for macro arguments in their documentation (@defmac).  
  15.  @macro dvar{varname, default}  
  16. -@r{[}@var{\varname\} = @samp{\default\}@r{]}@c  
  17. +@r{[}@var{\varname\} = @samp{\default\}@r{]}  
  18.  @end macro  
  19.    
  20.  @c Handling the indexes with Texinfo yields several different problems.  

根据这个补丁文件修改即可,直接修改源代码包,下次编译就不会再提示这个错误了。

3.host-makedevs

/opt/Android/a23androidSRC/lichee/out/linux/common/buildroot/build/host-makedevs/makedevs.c:374:6: error: variable ‘ret’ set but not used [-Werror=unused-but-set-variable]
  int ret = EXIT_SUCCESS;
      ^
cc1: all warnings being treated as errors

直接修改makedevs.c文件:

最后一行,return 0;

修改为:return ret;

源代码位置:./buildroot/package/makedevs/makedevs.c
posted @ 2016-06-23 16:48 lfc 阅读(65) | 评论 (0)编辑 收藏
  2016年5月18日
     摘要: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->static int mpegts_read_packet(AVFormatContext *s, AVPacket *pkt){xxxx &n...  阅读全文
posted @ 2016-05-18 11:30 lfc 阅读(82) | 评论 (0)编辑 收藏
  2016年5月4日

Android中的Looper类,是用来封装消息循环和消息队列的一个类,用于在android线程中进行消息处理。handler其实可以看做是一个工具类,用来向消息队列中插入消息的。


(1) Looper类用来为一个线程开启一个消息循环。     默认情况下android中新诞生的线程是没有开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环。)     Looper对象通过MessageQueue来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。 


(2) 通常是通过Handler对象来与Looper进行交互的。Handler可看做是Looper的一个接口,用来向指定的Looper发送消息及定义处理 方法。     默认情况下Handler会与其被定义时所在线程的Looper绑定,比如,Handler在主线程中定义,那么它是与主线程的Looper绑定。 mainHandler = new Handler() 等价于new Handler(Looper.myLooper()). Looper.myLooper():获取当前进程的looper对象,类似的 Looper.getMainLooper() 用于获取主线程的Looper对象。 


(3) 在非主线程中直接new Handler() 会报如下的错误: E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception E/AndroidRuntime( 6173): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() 原因是非主线程中默认没有创建Looper对象,需要先调用Looper.prepare()启用Looper。 


(4) Looper.loop(); 让Looper开始工作,从消息队列里取消息,处理消息。 


注意:写在Looper.loop()之后的代码不会被执行,这个函数内部应该是一个循环,当调用mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。


(5) 基于以上知识,可实现主线程给子线程(非主线程)发送消息。 

    把下面例子中的mHandler声明成类成员,在主线程通过mHandler发送消息即可。        

Android官方文档中Looper的介绍: Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.

Most interaction with a message loop is through the Handler class. 

This is a typical example of the implementation of a Looper thread, using the separation of prepare() and loop() to create an initial Handler to communicate with the Looper.

 1 class LooperThread extends Thread  
 2 {  
 3 public Handler mHandler;  
 4 public void run()   
 5 {  
 6 Looper.prepare();  
 7 mHandler = new Handler()   
 8 {  
 9 public void handleMessage(Message msg)   
10 {  
11 // process incoming messages here  
12 }  
13 };  
14 Looper.loop();  
15 }

如果线程中使用Looper.prepare()和Looper.loop()创建了消息队列就可以让消息处理在该线程中完成


android HandlerThread使用小例

之前研究过handler 和 looper 消息队列,不过android里的handler不是另外开启线程来执行的,还是在主UI线程中,如果想另启线程的话需要用到HandlerThread 来实现。在使用HandlerThread的时候需要实现CallBack接口以重写handlerMessage方法,在handlerMessage 方法中来处理自己的逻辑。下来给出一个小例子程序。

layout文件很简单,就一个按钮来启动HanlderTread线程


 1 <?xml version="1.0" encoding="utf-8"?>  
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
 3     android:layout_width="fill_parent"  
 4     android:layout_height="fill_parent"  
 5     android:orientation="vertical" >  
 6   
 7     <TextView  
 8         android:layout_width="fill_parent"  
 9         android:layout_height="wrap_content"  
10         android:text="@string/hello" />  
11   
12     <Button  
13         android:id="@+id/handlerThreadBtn"  
14         android:layout_width="wrap_content"  
15         android:layout_height="wrap_content"  
16         android:text="startHandlerThread" />  
17   
18 </LinearLayout>

Activity代码如下:


 1     package com.tayue;  
 2       
 3     import android.app.Activity;  
 4     import android.os.Bundle;  
 5     import android.os.Handler;  
 6     import android.os.Handler.Callback;  
 7     import android.os.HandlerThread;  
 8     import android.os.Message;  
 9     import android.view.View;  
10     import android.view.View.OnClickListener;  
11     import android.widget.Button;  
12     /** 
13      *  
14      * @author xionglei 
15      * 
16      */  
17     public class TestHandlerActivity extends Activity implements OnClickListener{  
18           
19         public Button handlerThreadBTN;   
20         MyHandlerThread handlerThread;  
21         Handler handler;  
22           
23         /** Called when the activity is first created. */  
24         @Override  
25         public void onCreate(Bundle savedInstanceState) {  
26             super.onCreate(savedInstanceState);  
27             //打印UI线程的名称  
28             System.out.println("onCreate  CurrentThread = " + Thread.currentThread().getName());  
29               
30             setContentView(R.layout.main);  
31               
32             handlerThreadBTN = (Button) findViewById(R.id.handlerThreadBtn);  
33             handlerThreadBTN.setOnClickListener(this);  
34               
35             handlerThread = new MyHandlerThread("myHanler");  
36             handlerThread.start();  
37             //注意: 这里必须用到handler的这个构造器,因为需要把callback传进去,从而使自己的HandlerThread的handlerMessage来替换掉Handler原生的handlerThread  
38             handler = new Handler(handlerThread.getLooper(), handlerThread);         
39         }  
40       
41         @Override  
42         public void onClick(View v) {  
43             //点击按钮后来开启线程  
44             handler.sendEmptyMessage(1);  
45         }      
46           
47         private class MyHandlerThread extends HandlerThread implements Callback {  
48       
49             public MyHandlerThread(String name) {  
50                 super(name);  
51             }  
52       
53             @Override  
54             public boolean handleMessage(Message msg) {  
55                 //打印线程的名称  
56                 System.out.println(" handleMessage CurrentThread = " + Thread.currentThread().getName());  
57                 return true;  
58             }              
59         }           
60     } 

点击按钮,打印的日志如下(这里点击了3次) 07-06 09:32:48.776: I/System.out(780): onCreate  CurrentThread = main 07-06 09:32:55.076: I/System.out(780):  handleMessage CurrentThread = myHanler 07-06 09:32:58.669: I/System.out(780):  handleMessage CurrentThread = myHanler 07-06 09:33:03.476: I/System.out(780):  handleMessage CurrentThread = myHanler

HandlerThread就这么简单。

当然 android自己也有异步线程的handler,就是AsyncTask,这个类就是封装了HandlerThread 和handler来实现异步多线程的操作的。

同样可以这样使用:


 1 private boolean iscancel = false//用户手动取消登录的标志位  
 2   
 3     handlerThread = new HandlerThread("myHandlerThread");  
 4                     handlerThread.start();  
 5                     handler = new MyHandler(handlerThread.getLooper());  
 6                 // 将要执行的线程对象添加到线程队列中  
 7                         handler.post(new Runnable() {  
 8                             @Override  
 9                             public void run() {  
10                                 Message message = handler.obtainMessage();  
11                                 UserBean user = Bbs.getInstance().Login(username, password);//耗时任务  
12                                 Bundle b = new Bundle();  
13                                 b.putSerializable("user", user);  
14                                 message.setData(b);  
15                                 message.sendToTarget(); //或使用 handler.sendMessage(message);  
16                             }  
17                         });  
18     class MyHandler extends Handler {  
19   
20             public MyHandler(Looper looper) {  
21                 super(looper);  
22             }  
23   
24             @Override  
25             public void handleMessage(Message msg) {  
26                 if(iscancel == false){  
27                     // 操作UI线程的代码  
28                     Bundle b = msg.getData();  
29                     UserBean user = (UserBean)b.get("user");  
30                                        
31                }  
32            }  
33     }
posted @ 2016-05-04 08:58 lfc 阅读(114) | 评论 (0)编辑 收藏
  2016年4月19日
posted @ 2016-04-19 16:41 lfc 阅读(61) | 评论 (0)编辑 收藏
  2016年3月31日
posted @ 2016-03-31 14:52 lfc 阅读(56) | 评论 (0)编辑 收藏
  2016年3月25日

1、Service的种类

  

按运行地点分类:

类别 区别  优点 缺点   应用
本地服务(Local) 该服务依附在主进程上,  服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,也不需要AIDL。相应bindService会方便很多。  主进程被Kill后,服务便会终止。  非常常见的应用如:HTC的音乐播放服务,天天动听音乐播放服务。
远程服务(Remote 该服务是独立的进程,  服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。  该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。  一些提供系统服务的Service,这种Service是常驻的。

其实remote服务还是很少见的,并且一般都是系统服务。

  

按运行类型分类:

类别 区别 应用
前台服务 会在通知一栏显示 ONGOING 的 Notification, 当服务被终止的时候,通知一栏的 Notification 也会消失,这样对于用户有一定的通知作用。常见的如音乐播放服务。
后台服务 默认的服务即为后台服务,即不会在通知一栏显示 ONGOING 的 Notification。 当服务被终止的时候,用户是看不到效果的。某些不需要运行或终止提示的服务,如天气更新,日期同步,邮件同步等。

有同学可能会问,后台服务我们可以自己创建 ONGOING 的 Notification 这样就成为前台服务吗?答案是否定的,前台服务是在做了上述工作之后需要调用 startForeground ( android 2.0 及其以后版本 )或 setForeground (android 2.0 以前的版本)使服务成为 前台服务。这样做的好处在于,当服务被外部强制终止掉的时候,ONGOING 的 Notification 任然会移除掉。

 

按使用方式分类:

类别 区别
startService 启动的服务 主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService
bindService 启动的服务 该方法启动的服务要进行通信。停止服务使用unbindService
startService 同时也 bindService 启动的服务 停止服务应同时使用stepService与unbindService

以上面三种方式启动的服务其生命周期也有区别,将在随后给出。

 

2、Service 与 Thread 的区别

  

很多时候,你可能会问,为什么要用 Service,而不用 Thread 呢,因为用 Thread 是很方便的,比起 Service 也方便多了,下面我详细的来解释一下。

 

1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。

2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。因此请不要把 Service 理解成线程,它跟线程半毛钱的关系都没有!

  

既然这样,那么我们为什么要用 Service 呢?其实这跟 android 的系统机制有关,我们先拿 Thread 来说。Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。

  

举个例子:如果你的 Thread 需要不停地隔一段时间就要连接服务器做某种同步的话,该 Thread 需要在 Activity 没有start的时候也在运行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制之前创建的 Thread。因此你便需要创建并启动一个 Service ,在 Service 里面创建、运行并控制该 Thread,这样便解决了该问题(因为任何 Activity 都可以控制同一 Service,而系统也只会创建一个对应 Service 的实例)。

  

因此你可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。

  

3、Service的生命周期

 

onCreate  onStart  onDestroy  onBind 

1). 被启动的服务的生命周期:如果一个Service被某个Activity 调用 Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService 方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。

 

2). 被绑定的服务的生命周期:如果一个Service被某个Activity 调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。

 

3). 被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会停止Service,而必须调用 stopService 或 Service的 stopSelf 来停止服务。

 

4). 当服务被停止时清除服务:当一个Service被终止(1、调用stopService;2、调用stopSelf;3、不再有绑定的连接(没有被启动))时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。

 

特别注意:

1、你应当知道在调用 bindService 绑定到Service的时候,你就应当保证在某处调用 unbindService 解除绑定(尽管 Activity 被 finish 的时候绑定会自      动解除,并且Service会自动停止);

2、你应当注意 使用 startService 启动服务之后,一定要使用 stopService停止服务,不管你是否使用bindService; 

3、同时使用 startService 与 bindService 要注意到,Service 的终止,需要unbindService与stopService同时调用,才能终止 Service,不管 startService 与 bindService 的调用顺序,如果先调用 unbindService 此时服务不会自动终止,再调用 stopService 之后服务才会停止,如果先调用 stopService 此时服务也不会终止,而再调用 unbindService 或者 之前调用 bindService 的 Context 不存在了(如Activity 被 finish 的时候)之后服务才会自动停止;

4、当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你的 Activity 如果会自动旋转的话,旋转其实是 Activity 的重新创建,因此旋转之前的使用 bindService 建立的连接便会断开(Context 不存在了),对应服务的生命周期与上述相同。

5、在 sdk 2.0 及其以后的版本中,对应的 onStart 已经被否决变为了 onStartCommand,不过之前的 onStart 任然有效。这意味着,如果你开发的应用程序用的 sdk 为 2.0 及其以后的版本,那么你应当使用 onStartCommand 而不是 onStart。

 

4、startService 启动服务

  

想要用 startService  启动服务,不管Local 还是 Remote 我们需要做的工作都是一样简单。当然要记得在 Androidmanifest.xml 中注册 service。

根据上面的生命周期,我们便会给出 Service 中的代码框架:

对应生命周期系统回调函数上面已经说明,在对应地方加上适当的代码即可。下面是启动与停止 Service 的代码:

对应的 Intent 为标志服务类的 Intent。

 

5、Local 与 Remote 服务绑定

 

同样记得在 Androidmanifest.xml 中注册 service

1). Local 服务绑定:Local 服务的绑定较简单,首先在 Service 中我们需要实现 Service 的抽象方法 onBind,并返回一个实现 IBinder 接口的对象。

Service 中的代码:

上面的代码关键之处,在于 onBind(Intent) 这个方法 返回了一个实现了 IBinder 接口的对象,这个对象将用于绑定Service 的 Activity 与 Local Service 通信。下面是 Activity 中的代码:

在 Activity 中,我们通过 ServiceConnection 接口来取得建立连接 与 连接意外丢失的回调。bindService有三个参数,第一个是用于区分 Service 的Intent 与 startService 中的 Intent 一致,第二个是实现了 ServiceConnection 接口的对象,最后一个是 flag 标志位。有两个flag,BIND_DEBUG_UNBIND 与 BIND_AUTO_CREATE,前者用于调试(详细内容可以查看javadoc 上面描述的很清楚),后者默认使用。unbindService 解除绑定,参数则为之前创建的 ServiceConnection 接口对象。另外,多次调用 unbindService 来释放相同的连接会抛出异常,因此我创建了一个 boolean 变量来判断是否 unbindService 已经被调用过。

运行结果:

 

2). Remote 服务绑定:Remote 的服务绑定由于服务是在另外一个进程,因此需要用到 android 的 IPC 机制。这将又是一个很长的话题,因此,我打算写另外一篇 android 的 IPC 机制分析 ,并在其中进行详述,然后在这里更新链接,敬请关注。

 

特别注意:

1、Service.onBind如果返回null,则调用 bindService 会启动 Service,但不会连接上 Service,因此 ServiceConnection.onServiceConnected 不会被调用,但你任然需要使用 unbindService 函数断开它,这样 Service 才会停止。

 

 6、创建前台服务

  

前台服务的优点上面已经说明,但设置服务为前台服务,我们需要注意在 sdk 2.0 及其以后版本使用的方法是 startForeground 与 stopForeground,之前版本使用的是 setForeground ,因此如果你应用程序的最低运行环境要求是 2.0,那么这里可以直接运用新方法,如果运行环境是2.0以下,那么为了保证向后兼容性,这里必须使用反射技术来调用新方法。

下面是我仿照 ApiDemos 重新敲的代码,对某些地方进行了修改,因此更具有说明性:

特别注意:

  1、使用 startForeground ,如果 id 为 0 ,那么 notification 将不会显示。

  

7、在什么情况下使用 startService 或 bindService 或 同时使用startService 和 bindService

  

如果你只是想要启动一个后台服务长期进行某项任务那么使用 startService 便可以了。如果你想要与正在运行的 Service 取得联系,那么有两种方法,一种是使用 broadcast ,另外是使用 bindService ,前者的缺点是如果交流较为频繁,容易造成性能上的问题,并且 BroadcastReceiver 本身执行代码的时间是很短的(也许执行到一半,后面的代码便不会执行),而后者则没有这些问题,因此我们肯定选择使用 bindService(这个时候你便同时在使用 startService 和 bindService 了,这在 Activity 中更新 Service 的某些运行状态是相当有用的)。另外如果你的服务只是公开一个远程接口,供连接上的客服端(android 的 Service 是C/S架构)远程调用执行方法。这个时候你可以不让服务一开始就运行,而只用 bindService ,这样在第一次 bindService 的时候才会创建服务的实例运行它,这会节约很多系统资源,特别是如果你的服务是Remote Service,那么该效果会越明显(当然在 Service 创建的时候会花去一定时间,你应当注意到这点)。

  

 

8、在 AndroidManifest.xml 里 Service 元素的常见选项

android:name  -------------  服务类名

android:label  --------------  服务的名字,如果此项不设置,那么默认显示的服务名则为类名

android:icon  --------------  服务的图标

android:permission  -------  申明此服务的权限,这意味着只有提供了该权限的应用才能控制或连接此服务

android:process  ----------  表示该服务是否运行在另外一个进程,如果设置了此项,那么将会在包名后面加上这段字符串表示另一进程的名字

android:enabled  ----------  如果此项设置为 true,那么 Service 将会默认被系统启动,不设置默认此项为 false

android:exported  ---------  表示该服务是否能够被其他应用程序所控制或连接,不设置默认此项为 false

 

本文代码下载地址:http://files.cnblogs.com/newcj/ServiceTest.zip

posted @ 2016-03-25 11:09 lfc 阅读(166) | 评论 (0)编辑 收藏
仅列出标题  下一页