Bytes de vídeo HTML5 no iOS

bytes-video-html5-ios-topo

O HTML5 fornece o elemento VIDEO. Ele inclui o atributo PRELOAD que tira vantagem de vários valores tais como “none”, “metadata” e “auto”. Dispositivos móveis ignoram todos os valores do PRELOAD de forma a evitar o alto custo dos planos de dados e, em vez disso, baixam o vídeo apenas quando o usuário começa a tocá-lo. Isso é explicado no Safari Developer Library:

No Safari para iOS (em todos os dispositivos, incluindo o iPad) onde o usuário pode estar eu uma rede de celular e ser cobrado por unidade de dados, atributos preload e autoplay são desabilitados. Nenhum dado é carregado até que o usuário inicie o vídeo.

Entretanto, meus testes demonstram que o iOS baixa até 298K de dados de vídeo, resultando em custos inesperados para os usuários.

Contradição?

Em meu artigo anterior, Pré-carregamento de vídeo em HTML5, analisei a quantidade de dados armazenada em buffer por vários valores do atributo PRELOAD da tag VIDEO. Como exemplo, especificar preload=’none’ assegura que nada é carregado previamente, onde preload=’auto’ resulta em 25 ou mais segundos de vídeo em buffer nos navegadores para desktop, dependendo do tamanho do vídeo.

Os resultados para navegadores móveis são diferentes. Navegadores móveis não carregam previamente nenhum dado de vídeo, não importa que valor é atribuído ao PRELOAD. Esses resultados de preload são baseados em testes com a propriedade buffered do elemento VIDEO. Utilizar essa API demonstra que zero bytes de dados são colocados em buffer em dispositivos móveis, incluindo o iPhone. (procure por “Mobile Safari 6” e uma dúzia de outros dispositivos móveis nos resultados detalhados.)

Enquanto é verdade que o Safari Mobile no iOS não faz buffer de qualquer dado de vídeo como resultado do atributo PRELOAD, ele faz outras requisições de vídeo que não são contadas como “buffer” de vídeo. O número e o tamanho das requisições e das respostas dependem dos vídeos. Para vídeos maiores, a quantidade total de dados para essas requisições “de bastidores” pode ser significante.

Requisições “invisíveis” de VIDEO

[Esta seção contém detalhes técnicos de como eu encontrei essas requisições de vídeo. Vá para "Confirmando os resultados e mais informações", se quiser pular esses detalhes.]

Ao testar o VIDEO PRELOAD no meu iPhone, percebi que mesmo que a quantidade de dados de buffer estivesse ajustada para “0”, havia múltiplas requisições de vídeo chegando até o meu servidor. Aqui estão as requisições de vídeo que vi em meu log de acesso do Apache quando carreguei a página de teste com preload=’none’(4M vídeo) no meu iPhone (iPhone4 com iOS6 executando o Safari Mobile padrão).

[16/Apr/2013:15:03:48 -0700] "GET /tests/trailer.mp4?t=1366149827 HTTP/1.1" 206 319 "-" "AppleCoreMedia/1.0.0.10B146 (iPhone; U; CPU OS 6_1_2 like Mac OS X; en_us)"
[16/Apr/2013:15:03:48 -0700] "GET /tests/trailer.mp4?t=1366149827 HTTP/1.1" 206 70080 "-" "AppleCoreMedia/1.0.0.10B146 (iPhone; U; CPU OS 6_1_2 like Mac OS X; en_us)"
[16/Apr/2013:15:03:48 -0700] "GET /tests/trailer.mp4?t=1366149827 HTTP/1.1" 206 47330 "-" "AppleCoreMedia/1.0.0.10B146 (iPhone; U; CPU OS 6_1_2 like Mac OS X; en_us)"

Há três requisições. Todas elas retornam um código de status “206 Partial Content”. Os tamanhos das respostas exibidas no log de acesso são de 319 bytes, 70.080 bytes e 47.330 bytes, respectivamente.

É possível que o servidor envie os bytes, mas o cliente (meu iPhone) não receba todos os pacotes. Uma vez que essas requisições de vídeo não são reflexo da utilização da API do elemento VIDEO, eu medi os bytes que foram realmente enviados sobre a conexão através do tcpdump de um hotspot wifi. (veja os detalhes da configuração aqui). Isso gera um arquivo pcap que nomeei como mediaevents-iphone.pcap. O primeiro passo é encontrar as conexões usadas para fazer as requisições de vídeo utilizando:

tcpdump -qns 0 -A -r mediaevents-iphone.pcap

A saída exibe os cabeçalhos HTTP e os dados de respostas para cada requisição. Posso encontrar três requisições de vídeo.

Requisição de vídeo nº 1

Aqui está o trecho relevante para a primeira requisição de vídeo (com alguns dados binários removidos)

15:03:48.162970 IP 192.168.2.2.49186 > 69.163.242.68.80: tcp 327
GET /tests/trailer.mp4?t=1366149827 HTTP/1.1
Host: stevesouders.com
Range: bytes=0-1
X-Playback-Session-Id: E4C46EAC-17EC-491E-81F7-4EBF4B7BE12B
Accept-Encoding: identity
Accept: */*
Accept-Language: en-us
Connection: keep-alive
User-Agent: AppleCoreMedia/1.0.0.10B146 (iPhone; U; CPU OS 6_1_2 like Mac OS X; en_us)

15:03:48.183732 IP 69.163.242.68.80 > 192.168.2.2.49186: tcp 0
15:03:48.186327 IP 69.163.242.68.80 > 192.168.2.2.49186: tcp 319
HTTP/1.1 206 Partial Content
Date: Tue, 16 Apr 2013 22:03:48 GMT
Server: Apache
Last-Modified: Thu, 13 May 2010 17:49:03 GMT
ETag: "42b795-4867d5fcac1c0"
Accept-Ranges: bytes
Content-Length: 2
Content-Range: bytes 0-1/4372373
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: video/mp4

Eu destaquei algumas informações importantes. Essa primeira requisição pelo trailer.mp4 acontece na conexão nº 49786. O iPhone requisita apenas os bytes 0-1, e o servidor retorna apenas os dois bytes. A abrangência do conteúdo do cabeçalho de resposta também indica o tamanho total do vídeo: 4,372,373 bytes (~4.2M).

Requisição de vídeo nº 2

Aqui está o trecho relevante para a segunda requisição de vídeo:

15:03:48.325209 IP 192.168.2.2.49186 > 69.163.242.68.80: tcp 333
GET /tests/trailer.mp4?t=1366149827 HTTP/1.1
Host: stevesouders.com
Range: bytes=0-4372372
X-Playback-Session-Id: E4C46EAC-17EC-491E-81F7-4EBF4B7BE12B
Accept-Encoding: identity
Accept: */*
Accept-Language: en-us
Connection: keep-alive
User-Agent: AppleCoreMedia/1.0.0.10B146 (iPhone; U; CPU OS 6_1_2 like Mac OS X; en_us)

15:03:48.349254 IP 69.163.242.68.80 > 192.168.2.2.49186: tcp 1460
HTTP/1.1 206 Partial Content
Date: Tue, 16 Apr 2013 22:03:48 GMT
Server: Apache
Last-Modified: Thu, 13 May 2010 17:49:03 GMT
ETag: "42b795-4867d5fcac1c0"
Accept-Ranges: bytes
Content-Length: 4372373
Content-Range: bytes 0-4372372/4372373
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: video/mp4

Essa requisição também utiliza a conexão nº 49186. O iPhone requisita os bytes 0-4372372 (o vídeo inteiro). A extensão do conteúdo implica que os 4,372,373 bytes são retornados, mas logo veremos que é muito menos do que isso.

Requisição de vídeo nº 3

Aqui está o trecho relevante para a terceira requisição:

15:03:48.409745 IP 192.168.2.2.49187 > 69.163.242.68.80: tcp 339
GET /tests/trailer.mp4?t=1366149827 HTTP/1.1
Host: stevesouders.com
Range: bytes=4325376-4372372
X-Playback-Session-Id: E4C46EAC-17EC-491E-81F7-4EBF4B7BE12B
Accept-Encoding: identity
Accept: */*
Accept-Language: en-us
Connection: keep-alive
User-Agent: AppleCoreMedia/1.0.0.10B146 (iPhone; U; CPU OS 6_1_2 like Mac OS X; en_us)

15:03:48.435684 IP 69.163.242.68.80 > 192.168.2.2.49187: tcp 0
15:03:48.438211 IP 69.163.242.68.80 > 192.168.2.2.49187: tcp 1460
HTTP/1.1 206 Partial Content
Date: Tue, 16 Apr 2013 22:03:48 GMT
Server: Apache
Last-Modified: Thu, 13 May 2010 17:49:03 GMT
ETag: "42b795-4867d5fcac1c0"
Accept-Ranges: bytes
Content-Length: 46997
Content-Range: bytes 4325376-4372372/4372373
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: video/mp4

A terceira requisição é feita por uma nova conexão nº 49187. O iPhone requisita os últimos 46,997 bytes de vídeo. Isso é provavelmente relacionado aos metadados do vídeo (ou “moov atom”).

Tamanho total do pacote

Tomei nota do número das conexões porque em meus próximos passos fiz uso deles para visualizar os arquivos de pacotes de vídeo utilizando este comando:

tcpdump -r mediaevents-iphone.pcap | grep -E "(49186|49187)"

Aqui está a saída do comando. É longa:

15:03:48.147958 IP 192.168.2.2.49186 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 1, win 16384, length 0
15:03:48.162970 IP 192.168.2.2.49186 > apache2-pat.esp.dreamhost.com.http: Flags [P.], seq 1:328, ack 1, win 16384, length 327
15:03:48.183732 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49186: Flags [.], ack 328, win 14, length 0
15:03:48.186327 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49186: Flags [P.], seq 1:320, ack 328, win 14, length 319
15:03:48.190322 IP 192.168.2.2.49186 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 320, win 16364, length 0
15:03:48.325209 IP 192.168.2.2.49186 > apache2-pat.esp.dreamhost.com.http: Flags [P.], seq 328:661, ack 320, win 16384, length 333
15:03:48.349254 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49186: Flags [.], seq 320:1780, ack 661, win 16, length 1460
15:03:48.349480 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49186: Flags [.], seq 1780:3240, ack 661, win 16, length 1460
15:03:48.349520 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49186: Flags [.], seq 3240:4700, ack 661, win 16, length 1460
15:03:48.350034 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49186: Flags [.], seq 4700:6160, ack 661, win 16, length 1460
15:03:48.355531 IP 192.168.2.2.49186 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 3240, win 16292, length 0
15:03:48.355699 IP 192.168.2.2.49186 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 6160, win 16110, length 0
15:03:48.366281 IP 192.168.2.2.49186 > apache2-pat.esp.dreamhost.com.http: Flags [F.], seq 661, ack 6160, win 16384, length 0
15:03:48.378012 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49186: Flags [.], seq 6160:7620, ack 661, win 16, length 1460
15:03:48.378086 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49186: Flags [.], seq 7620:9080, ack 661, win 16, length 1460
15:03:48.378320 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [S], seq 3267277275, win 65535, options [mss 1460,nop,wscale 4,nop,nop,TS val 129308742 ecr 0,sack\
OK,eol], length 0
15:03:48.378417 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49186: Flags [.], seq 9080:10540, ack 661, win 16, length 1460
15:03:48.378675 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49186: Flags [.], seq 10540:12000, ack 661, win 16, length 1460
15:03:48.379705 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49186: Flags [.], seq 12000:13460, ack 661, win 16, length 1460
15:03:48.379760 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49186: Flags [.], seq 13460:14920, ack 661, win 16, length 1460
15:03:48.383237 IP 192.168.2.2.49186 > apache2-pat.esp.dreamhost.com.http: Flags [R], seq 3028115607, win 0, length 0
15:03:48.383430 IP 192.168.2.2.49186 > apache2-pat.esp.dreamhost.com.http: Flags [R], seq 3028115607, win 0, length 0
15:03:48.383761 IP 192.168.2.2.49186 > apache2-pat.esp.dreamhost.com.http: Flags [R], seq 3028115607, win 0, length 0
15:03:48.384064 IP 192.168.2.2.49186 > apache2-pat.esp.dreamhost.com.http: Flags [R], seq 3028115607, win 0, length 0
15:03:48.384297 IP 192.168.2.2.49186 > apache2-pat.esp.dreamhost.com.http: Flags [R], seq 3028115607, win 0, length 0
15:03:48.385553 IP 192.168.2.2.49186 > apache2-pat.esp.dreamhost.com.http: Flags [R], seq 3028115607, win 0, length 0
15:03:48.399069 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [S.], seq 277046834, ack 3267277276, win 5840, options [mss 1460,nop,nop,sackOK,nop,wscale 9], len\
gth 0
15:03:48.401299 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 1, win 16384, length 0
15:03:48.409745 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [P.], seq 1:340, ack 1, win 16384, length 339
15:03:48.435684 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], ack 340, win 14, length 0
15:03:48.438211 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 1:1461, ack 340, win 14, length 1460
15:03:48.438251 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 1461:2921, ack 340, win 14, length 1460
15:03:48.438671 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 2921:4381, ack 340, win 14, length 1460
15:03:48.444261 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 2921, win 16201, length 0
15:03:48.446763 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 4381, win 16384, length 0
15:03:48.464208 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 4381:5841, ack 340, win 14, length 1460
15:03:48.464589 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 5841:7301, ack 340, win 14, length 1460
15:03:48.465088 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 7301:8761, ack 340, win 14, length 1460
15:03:48.469165 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 8761:10221, ack 340, win 14, length 1460
15:03:48.469269 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 10221:11681, ack 340, win 14, length 1460
15:03:48.474194 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 7301, win 16201, length 0
15:03:48.474491 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 10221, win 16019, length 0
15:03:48.481544 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 11681, win 16384, length 0
15:03:48.495319 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 11681:13141, ack 340, win 14, length 1460
15:03:48.495878 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 13141:14601, ack 340, win 14, length 1460
15:03:48.495928 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 14601:16061, ack 340, win 14, length 1460
15:03:48.500477 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 16061:17521, ack 340, win 14, length 1460
15:03:48.500542 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 17521:18981, ack 340, win 14, length 1460
15:03:48.500743 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 18981:20441, ack 340, win 14, length 1460
15:03:48.503543 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 14601, win 16201, length 0
15:03:48.504110 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 17521, win 16019, length 0
15:03:48.508600 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 20441, win 16201, length 0
15:03:48.524397 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 20441:21901, ack 340, win 14, length 1460
15:03:48.524753 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 21901:23361, ack 340, win 14, length 1460
15:03:48.524773 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 23361:24821, ack 340, win 14, length 1460
15:03:48.525207 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 24821:26281, ack 340, win 14, length 1460
15:03:48.525756 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 26281:27741, ack 340, win 14, length 1460
15:03:48.533224 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 23361, win 16201, length 0
15:03:48.533395 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 26281, win 16019, length 0
15:03:48.533553 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 27741, win 16384, length 0
15:03:48.533714 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 27741:29201, ack 340, win 14, length 1460
15:03:48.534656 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 29201:30661, ack 340, win 14, length 1460
15:03:48.534753 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 30661:32121, ack 340, win 14, length 1460
15:03:48.534835 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 32121:33581, ack 340, win 14, length 1460
15:03:48.535268 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 33581:35041, ack 340, win 14, length 1460
15:03:48.535933 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 35041:36501, ack 340, win 14, length 1460
15:03:48.540701 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 30661, win 16292, length 0
15:03:48.540792 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 33581, win 16110, length 0
15:03:48.540966 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 36501, win 15927, length 0
15:03:48.555942 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 36501:37961, ack 340, win 14, length 1460
15:03:48.556276 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 37961:39421, ack 340, win 14, length 1460
15:03:48.556767 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 39421:40881, ack 340, win 14, length 1460
15:03:48.556810 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 40881:42341, ack 340, win 14, length 1460
15:03:48.557342 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 42341:43801, ack 340, win 14, length 1460
15:03:48.557818 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 43801:45261, ack 340, win 14, length 1460
15:03:48.565012 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 39421, win 16201, length 0
15:03:48.565188 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 42341, win 16019, length 0
15:03:48.565232 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 45261, win 15836, length 0
15:03:48.565417 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [.], seq 45261:46721, ack 340, win 14, length 1460
15:03:48.565477 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [P.], seq 46721:47331, ack 340, win 14, length 610
15:03:48.571326 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 46721, win 16384, length 0
15:03:48.571664 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 47331, win 16345, length 0
15:03:50.444574 IP apache2-pat.esp.dreamhost.com.http > 192.168.2.2.49187: Flags [F.], seq 47331, ack 340, win 14, length 0
15:03:50.471892 IP 192.168.2.2.49187 > apache2-pat.esp.dreamhost.com.http: Flags [.], ack 47332, win 16384, length 0

Há muitas coisas interessantes para ver aqui, mas, para não fugir do assunto, vamos imaginar quantos dados de vídeo foram realmente recebidos pelo iPhone. Vamos olhar os pacotes que o servidor (dreamhost.com) enviou para o meu iPhone (192.168.2.2) e acrescentar o tamanho dos valores. O total chega a 62,249 bytes (~61K). Separando isso por requisições, chegamos a 319 bytes para a primeira resposta, 14.600 bytes para a segunda e 47.330 bytes para a terceira resposta. Vamos comparar os valores reais com os que vimos nos logs de acesso do Apache e ver qual foi o tamanho dos conteúdos dos cabeçalhos de resposta:

Apache access log Content-Length actual bytes received
request #1 319 bytes 2 bytes 319 bytes
request #2 70,080 bytes 4,372,373 bytes 14,600 bytes
request #3 47,330 bytes 46,997 bytes 47,330 bytes

O que isso nos diz é que você não pode confiar sempre no que é exibido nos logs de acesso do servidor e mesmo no conteúdo dos cabeçalhos. Os tamanhos das requisições 1 e 3 são consistentes quando você leva em conta que o tamanho dos cabeçalhos não está incluído no conteúdo da mensagem. O tamanho da requisição número 2 está em todo lugar. Olhar os pacotes em maior detalhe revela o porquê: o iPhone tentou fechar a conexão depois de receber 5.840 bytes e então finalmente reiniciou a conexão depois de 14.600 bytes. Por conta disso, o tamanho atual dos dados baixados foi diferente dos logs de acesso e do conteúdo indicado.

A conclusão é que, em alguns dias, você precisa descer até o nível da camada do tcpdum e dos arquivos pcap para encontrar a verdade. Para isso, aproximadamente 4M de vídeo nos arquivos pcap demonstram que o iOS baixou aproximadamente 61K de dados do vídeo. (Obrigado, Arvind Jain, por me ajudar a decifrar os arquivos pcap!)

Confirmando os resultados e mais informações

Aqui estão algumas informações adicionais dos meus testes.

Vídeos grandes => mais dados: A quantidade de dados baixados pelo iPhone pode aumentar para vídeos grandes. O teste do preload=’none’ para um vídeo de 62M gerou sete requisições de vídeo. A mesma técnica utilizada com o tcpdump demonstrou que esse vídeo maior resultou em 304.918 bytes (~298K) de dados baixados.

Dados “não reutilizados”: Para piorar as coisas, esses dados de vídeo que são baixados nos bastidores não são utilizados quando o usuário inicia a execução do vídeo. Para testar isso, eu iniciei uma nova captura do tcpdump e dei play no vídeo de 4.2M. Isso resultou em um novo download de 4,401,911 bytes (~4.2M) – o tamanho total do vídeo.

Acontece em redes de celular: todos os testes até aqui foram feitos com wifi, mas cobranças de planos de dados acontecem apenas em redes celulares. Uma possibilidade é que o iPhone baixe esses dados de vídeo apenas em conexões wifi, mas não nas redes das operadoras. Infelizmente, esse não é o caso. Testei isso ao desligar o wifi em meu iPhone fechando todos os apps, reiniciando as estatísticas de uso de redes móveis e carregando a página de teste cinco vezes.

Para a página de teste com preload=’none’ (vídeo de 4M), a quantidade de dados recebidos pela rede de celular foi de 354K em todos os carregamentos da página, ou 71K por página. A página de teste sem a tag VIDEO é de aproximadamente 8K, o que é próximo dos resultados encontrados anteriormente, onde ~61K de dados de vídeo foram baixados em segundo plano. Para a página de teste com o vídeo de 62M, a quantidade de dados recebida via celular foi de 1,6M para os cinco carregamentos, ou aproximadamente 320K por página. Depois de contabilizar o tamanho da página de teste e os erros de arredondamento, isso é próximo de 298K de dados de vídeo para o arquivo maior. A conclusão é que essas requisições “invisíveis” de vídeo ocorrem quando o iPhone está no wifi e em uma rede móvel.

Acontece apenas no iOS: Essa estranha requisição de vídeo não acontece no meu Samsung Galaxy Nexus (Android 4.2.2) utilizando o navegador padrão do Android, nem no Chrome para Android 26. Por conta de os testes manipularem os dispositivos manualmente, não posso testar outros telefones utilizando o navegador. Entretanto, sabendo que uma dúzia ou mais de diferentes dispositivos móveis executaram o teste no meu servidor, procurei nos logs de acesso a partir do ponto em que publiquei o artigo Précarregamento de vídeo em HTML5 para ver se algum deles gerou requisições pelo trailer.mp4. Os únicos que o fizeram foram o iPhone e o iPad, sugerindo que nenhum outro dispositivo móvel gerou essas requisições de vídeo em segundo plano.

User-Agent estranho e sem referência: o cabeçalho de requisição para o User-Agente do Safari mobile em meu iPhone é:

Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_2 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10B146 Safari/8536.25

Mas o User-Agent para as requisições de vídeo é:

AppleCoreMedia/1.0.0.10B146 (iPhone; U; CPU OS 6_1_2 like Mac OS X; en_us)

Eles são diferentes!

Meu Galaxy Nexus possui um comportamento semelhante. O User-Agent para o navegador do Android é:

Mozilla/5.0 (Linux; U; Android 4.2.2; en-us; Galaxy Nexus Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30

Chrome para Android é:

Mozilla/5.0 (Linux; Android 4.2.2; Galaxy Nexus Build/JDQ39) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.58 Mobile Safari/537.31

Mas ambos enviam esse User-Agent quando requisitam vídeo:

stagefright/1.2 (Linux;Android 4.2.2)

É bom ter isso em mente ao investigar requisições de vídeo em dispositivos móveis. Outro aborrecimento é que todos os três navegadores móveis omitem a referência de cabeçalho de requisição. Isso parece ser um claro descuido, o que torna difícil relacionar a execução de vídeos com as visualizações da página.

Sem uma boa solução

Mencionei este artigo para o meu colega de trabalho, e ele apontou os comentários de Jim Wilson em seu artigo intitulado Breaking the 1000ms Time to Glass Mobile Barrier. Parece que Jim também percebeu essas requisições “invisíveis” de vídeo. Ele foi mais fundo para encontrar um jeito de evitar a disputa por banda:

Descobrimos que se a tag <video> tiver qualquer informação, o dispositivo móvel irá requerê-la. O que queremos é que se algo precisasse começar logo de cara (ex.: exibir um poster) e se tivéssemos algo na tag vídeo, isso deixava o dispositivo mais lento. Da perspectiva do usuário, nada estava acontecendo.

Então, nossa última versão alinha estilos críticos no cabeçalho, utiliza <div> com background:url() para o poster, possui uma tag <video> vazia, carrega dinamicamente o Video.js e quando termina (onready/onload) ajusta o fonte através da api do Video.js.

Enfatizei a solução em negrito. A tag VIDEO não especifica o atributo SRC. Isso evita qualquer requisição de vídeo em segundo plano no iOS competindo pela banda com recursos que são visíveis na página (tais como uma imagem de POSTER). O SRC é ajustado mais tarde via JavaScript.

Enquanto essa técnica evita a competição por banda, ela não evita as requisições de vídeo. Quando o SRC é definido mais tarde, ela também resulta nos mesmos dados de vídeo que são baixados. De fato, quando testei essa técnica, ela na verdade resultou em ainda mais dados baixados, o que eleva o custo.

É possível que mover os metadados para a frente do arquivo de vídeo possa reduzir a quantidade de dados baixada. Os 19M de vídeo do Video.js são formatados dessa maneira e baixam menos dados que meu teste com o video de 4M. Arrumar os dados do MP4 de maneira mais eficiente deve ser tema de investigação futura para checar se isso reduz a quantidade de dados baixados.

Conclusão

Quando a tag VIDEO do HTML5 é utilizada, o iOS baixa dados de vídeo sem que o usuário tenha iniciado a execução do vídeo. Nos testes descritos aqui, a quantidade de dados de vídeo foi de 61K para 298K. Esse comportamento se diferencia de outros dispositivos móveis. Isso significa que, simplesmente visitar uma página que utiliza o elemento VIDEO com seu iPhone ou iPad, pode resultar em cobranças inesperadas de dados em redes de celular. Infelizmente, não há uma boa solução para evitar essas requisições desnecessárias de vídeo. Submeti um bug para a Apple pedindo que o iOS evite essa cobrança extra de dados de forma similar ao que fazem outros dispositivos móveis.

Fonte: Imasters

Postagens Relacionadas